From d9c6681d6fb97a2b034eae28b54a449bb766d029 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 05:14:37 +0200 Subject: [PATCH 01/14] feat(marketplace): distribute Magpie 0.2.0 via agent plugin marketplaces Package the framework as a single 'magpie' plugin (skills: ./skills, all 70) and add the manifests each agent marketplace needs, referencing the existing skills tree with no vendored copies: - Claude Code: .claude-plugin/marketplace.json + plugin.json - Codex CLI: .codex-plugin/plugin.json + .agents/plugins/marketplace.json - Copilot: marketplace.json (repo root) - Gemini CLI: gemini-extension.json (skills auto-discovered) - microsoft/apm: apm.yml (type: skill; multiplexes to Claude/Cursor/Codex/Copilot/Gemini) Adds docs/setup/marketplaces.md (per-agent install, incl. Kiro/OpenCode git-install and the Windsurf/Goose non-mappings), positions the marketplace path as a discovery/trial channel alongside /magpie-setup, keeps the ASF source release canonical, and wires the version-bearing manifests into version_manifest_files so release-prepare keeps them in sync. Refs #518. --- .agents/plugins/marketplace.json | 12 +++ .claude-plugin/marketplace.json | 13 +++ .claude-plugin/plugin.json | 19 +++++ .codex-plugin/plugin.json | 6 ++ README.md | 18 ++--- apm.yml | 26 ++++++ docs/setup/marketplaces.md | 83 ++++++++++++++++++++ gemini-extension.json | 5 ++ marketplace.json | 12 +++ projects/magpie/release-management-config.md | 2 +- 10 files changed, 186 insertions(+), 10 deletions(-) create mode 100644 .agents/plugins/marketplace.json create mode 100644 .claude-plugin/marketplace.json create mode 100644 .claude-plugin/plugin.json create mode 100644 .codex-plugin/plugin.json create mode 100644 apm.yml create mode 100644 docs/setup/marketplaces.md create mode 100644 gemini-extension.json create mode 100644 marketplace.json diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json new file mode 100644 index 000000000..defdb4373 --- /dev/null +++ b/.agents/plugins/marketplace.json @@ -0,0 +1,12 @@ +{ + "name": "apache-magpie", + "interface": { "displayName": "Apache Magpie" }, + "plugins": [ + { + "name": "magpie", + "source": { "source": "local", "path": "." }, + "category": "maintenance", + "policy": { "installation": "manual", "authentication": "none" } + } + ] +} diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 000000000..24b6940d7 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,13 @@ +{ + "name": "apache-magpie", + "owner": { "name": "Apache Magpie", "url": "https://magpie.apache.org/" }, + "description": "Apache Magpie agentic skills for maintaining open-source projects.", + "plugins": [ + { + "name": "magpie", + "source": ".", + "version": "0.2.0", + "description": "All Apache Magpie skills (release management, security, PR/issue workflows, contributor growth, repo health)." + } + ] +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 000000000..b9b7d0afc --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "magpie", + "version": "0.2.0", + "description": "Apache Magpie — a reusable, governance-agnostic framework of agentic skills for maintaining open-source projects: release management, security triage, PR and issue workflows, contributor growth, and repo health.", + "author": { "name": "Apache Magpie", "url": "https://magpie.apache.org/" }, + "homepage": "https://magpie.apache.org/", + "repository": "https://github.com/apache/magpie", + "license": "Apache-2.0", + "keywords": [ + "apache", + "agent-skills", + "maintenance", + "release-management", + "security", + "pr-management", + "open-source" + ], + "skills": "./skills" +} diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json new file mode 100644 index 000000000..48229064a --- /dev/null +++ b/.codex-plugin/plugin.json @@ -0,0 +1,6 @@ +{ + "name": "magpie", + "version": "0.2.0", + "description": "Apache Magpie — agentic skills for maintaining open-source projects.", + "skills": "./skills" +} diff --git a/README.md b/README.md index 6e0566caf..ca6481611 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,11 @@ review, mentoring contributors, managing security reports, and more. Magpie is distributed per the [ASF release policy](https://www.apache.org/legal/release-policy.html) (see [release-distribution](https://infra.apache.org/release-distribution.html) -for the canonical mechanism); you adopt it by pinning a release. - -> [!NOTE] -> A **public skill marketplace is planned but not yet implemented.** -> Today you adopt Magpie directly from the released source, not from a -> marketplace. +for the canonical mechanism). Two ways in: adopt a release with +`/magpie-setup` (full snapshot + override adoption), or install the skills +directly from your agent's plugin marketplace — Claude Code, Codex, Copilot, +Gemini, Cursor, and more. See +[`docs/setup/marketplaces.md`](docs/setup/marketplaces.md). > [!IMPORTANT] > The motivation, scope, and design commitments behind this work @@ -81,9 +80,10 @@ into their repo. That skill manages everything else: [`docs/setup/agentic-overrides.md`](docs/setup/agentic-overrides.md) for the contract. -**No git submodules. No marketplace (yet). No vendored copies of -framework skills.** Just one committed skill (the bootstrap), -a gitignored snapshot, and agent-readable override files. +**No git submodules. No vendored copies of framework skills.** Just one +committed skill (the bootstrap), a gitignored snapshot, and agent-readable +override files. (Prefer a marketplace? See +[`docs/setup/marketplaces.md`](docs/setup/marketplaces.md).) ## Adopting the framework diff --git a/apm.yml b/apm.yml new file mode 100644 index 000000000..14d5d6aca --- /dev/null +++ b/apm.yml @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: Apache-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 +# +# microsoft/apm (Agent Package Manager) package manifest. apm compiles this +# single source to multiple agent clients (Claude, Cursor, Codex, Copilot, +# Gemini). Schema is v0.1 and may change — verify against the current +# https://microsoft.github.io/apm/ before publishing. +name: magpie +version: 0.2.0 +type: skill +description: >- + Apache Magpie — a reusable, governance-agnostic framework of agentic skills + for maintaining open-source projects: release management, security triage, + PR and issue workflows, contributor growth, and repo health. +author: Apache Magpie +license: Apache-2.0 +homepage: https://magpie.apache.org/ +repository: https://github.com/apache/magpie +keywords: + - apache + - agent-skills + - maintenance + - release-management + - security +includes: + - skills/ diff --git a/docs/setup/marketplaces.md b/docs/setup/marketplaces.md new file mode 100644 index 000000000..39baceebc --- /dev/null +++ b/docs/setup/marketplaces.md @@ -0,0 +1,83 @@ + + + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Installing Apache Magpie from agent marketplaces](#installing-apache-magpie-from-agent-marketplaces) + - [Supported agents](#supported-agents) + - [Not supported](#not-supported) + - [Versioning](#versioning) + - [Verification status](#verification-status) + + + + + +# Installing Apache Magpie from agent marketplaces + +From 0.2.0, Apache Magpie ships manifests so its skills can be installed +through the plugin/extension mechanisms of the major AI coding agents, +**in addition to** the canonical `/magpie-setup` snapshot adoption (see +[`install-recipes.md`](install-recipes.md)). + +> [!IMPORTANT] +> The marketplace path is a **discovery and trial** channel: it drops the +> 70 skills into your agent so you can use them immediately. It does **not** +> set up the full adoption machinery (the committed pin, the gitignored +> snapshot, drift detection, agentic overrides, or the secure-agent setup). +> For a project that adopts Magpie for real, use `/magpie-setup` — the +> marketplace install and full adoption are complementary, not exclusive. + +> [!NOTE] +> The **canonical release** of Apache Magpie remains the signed source +> artefact on `dist.apache.org` per the +> [ASF release policy](https://www.apache.org/legal/release-policy.html). +> Marketplace entries are a convenience layer that reference the released +> `X.Y.Z` git tag; they are derived from — not a substitute for — the ASF +> source release. + +All entries package the whole framework as a single **`magpie`** plugin +that exposes every skill under the `skills/` tree. Skills are then invoked +with the installing agent's namespacing (e.g. `/magpie:release-vote-tally`). + +## Supported agents + +| Agent | Install | Manifest in this repo | +|---|---|---| +| **Claude Code** | `/plugin marketplace add apache/magpie` then `/plugin install magpie@apache-magpie` | `.claude-plugin/marketplace.json`, `.claude-plugin/plugin.json` | +| **OpenAI Codex CLI** | `codex plugin marketplace add apache/magpie` then install `magpie` | `.codex-plugin/plugin.json`, `.agents/plugins/marketplace.json` | +| **GitHub Copilot** | add the repo as a plugin marketplace, then install `magpie` | `marketplace.json` (repo root) | +| **Google Gemini CLI** | `gemini extensions install https://github.com/apache/magpie` | `gemini-extension.json` | +| **Cursor** | add via the plugin/skill install flow pointing at the repo | consumes the plugin manifests above | +| **microsoft/apm** | `apm install apache/magpie` (compiles to Claude/Cursor/Codex/Copilot/Gemini) | `apm.yml` | +| **Kiro** | install from the GitHub subdirectory of a skill (Kiro installs per-skill from a subdir, not the repo root) | native `skills//SKILL.md` | +| **OpenCode** | drop the skills into `.opencode/skills/`, or use a community installer against this repo | native `skills//SKILL.md` | + +### Not supported + +- **Windsurf** — has no skills/rules marketplace; project rules are plain + `.windsurfrules` files with no install mechanism. Skills would have to be + converted by hand; there is no distribution channel. +- **Goose (Block)** — its extension registry is Model Context Protocol + (MCP) servers, not `SKILL.md` skills. Distributing Magpie there would + require wrapping skills behind an MCP server (a rebuild, not packaging). + +## Versioning + +The plugin version tracks the framework release (`0.2.0`). The manifest +version strings are kept in sync with `pyproject.toml` by the +`release-prepare` version bump — see +[`release-management-config.md`](../../projects/magpie/release-management-config.md) +(`version_manifest_files`). + +## Verification status + +The Claude Code and Gemini CLI manifests follow the current published +schemas. The Codex CLI, GitHub Copilot, and `apm` (schema v0.1) formats +move quickly; re-check each against the vendor's current documentation +before a marketplace publish. Manifests that fail live validation should be +fixed here and re-released — they never change how the ASF source release +is built or signed. diff --git a/gemini-extension.json b/gemini-extension.json new file mode 100644 index 000000000..116dd7b24 --- /dev/null +++ b/gemini-extension.json @@ -0,0 +1,5 @@ +{ + "name": "magpie", + "version": "0.2.0", + "description": "Apache Magpie — agentic skills for maintaining open-source projects. Skills are auto-discovered from ./skills." +} diff --git a/marketplace.json b/marketplace.json new file mode 100644 index 000000000..798cfcb6e --- /dev/null +++ b/marketplace.json @@ -0,0 +1,12 @@ +{ + "name": "apache-magpie", + "owner": { "name": "Apache Magpie", "url": "https://magpie.apache.org/" }, + "description": "Apache Magpie agentic skills for maintaining open-source projects (GitHub Copilot plugin marketplace catalog).", + "plugins": [ + { + "name": "magpie", + "source": ".", + "description": "All Apache Magpie skills (release management, security, PR/issue workflows, contributor growth, repo health)." + } + ] +} diff --git a/projects/magpie/release-management-config.md b/projects/magpie/release-management-config.md index 7d084310d..f7b856b98 100644 --- a/projects/magpie/release-management-config.md +++ b/projects/magpie/release-management-config.md @@ -71,7 +71,7 @@ mandatory ASF approval + announce mechanisms (`dev-list-vote`, | `git_upstream_remote` | `upstream` | | `release_planning_issue_template` | *(none — uses the `release-prepare` default template)* | | `release_branch_base` | `main` | -| `version_manifest_files` | `pyproject.toml` | +| `version_manifest_files` | `pyproject.toml`, `uv.lock`, `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `.codex-plugin/plugin.json`, `gemini-extension.json`, `apm.yml` | ## Backends From 36f078d4c79cc6ce94660d4d569ffbeb4a192464 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 05:19:03 +0200 Subject: [PATCH 02/14] feat(marketplace): auto-detect plugin updates and prompt for upgrade Add a Claude Code SessionStart hook (hooks/check-upgrade.sh, wired via the plugin's hooks block) that detects when the marketplace has updated the Magpie plugin to a new version and prompts the user to run `/magpie-setup upgrade` to reconcile the snapshot, agentic overrides, and drift. Detect-and-prompt, not auto-run: Claude Code hooks cannot invoke a slash command, and Magpie never mutates an adopter repo without the guided skill's confirmation, so the trigger is automatic while the changes stay confirmed. Claude Code-only; Codex/Gemini have no equivalent lifecycle hook. --- .claude-plugin/plugin.json | 16 ++++++++++++- docs/setup/marketplaces.md | 21 +++++++++++++++++ hooks/check-upgrade.sh | 47 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100755 hooks/check-upgrade.sh diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index b9b7d0afc..12c029ba4 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -15,5 +15,19 @@ "pr-management", "open-source" ], - "skills": "./skills" + "skills": "./skills", + "hooks": { + "SessionStart": [ + { + "matcher": "startup", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/check-upgrade.sh", + "timeout": 10 + } + ] + } + ] + } } diff --git a/docs/setup/marketplaces.md b/docs/setup/marketplaces.md index 39baceebc..dfaf66620 100644 --- a/docs/setup/marketplaces.md +++ b/docs/setup/marketplaces.md @@ -8,6 +8,7 @@ - [Installing Apache Magpie from agent marketplaces](#installing-apache-magpie-from-agent-marketplaces) - [Supported agents](#supported-agents) - [Not supported](#not-supported) + - [Automatic upgrade detection (Claude Code)](#automatic-upgrade-detection-claude-code) - [Versioning](#versioning) - [Verification status](#verification-status) @@ -65,6 +66,26 @@ with the installing agent's namespacing (e.g. `/magpie:release-vote-tally`). (MCP) servers, not `SKILL.md` skills. Distributing Magpie there would require wrapping skills behind an MCP server (a rebuild, not packaging). +## Automatic upgrade detection (Claude Code) + +The Claude Code plugin ships a `SessionStart` hook +([`hooks/check-upgrade.sh`](../../hooks/check-upgrade.sh)) that fires when a +session starts. It compares the installed plugin version against a marker in +the plugin's persistent data directory and, when the marketplace has updated +the plugin to a new version, prompts you to run **`/magpie-setup upgrade`** — +which reconciles the gitignored snapshot, the agentic overrides, and drift. + +The hook **detects and prompts**; it does not run the upgrade itself. Claude +Code hooks cannot invoke a slash command, and — by design — Magpie never +mutates an adopter repo without the guided skill's confirmation, so the +*trigger* is automatic while the *changes* stay confirmed. The hook is +read-only apart from writing its own version marker, makes no network calls, +and touches nothing in the adopter repo. + +This lifecycle wiring is **Claude Code-only**: Codex CLI and Gemini CLI have +no equivalent plugin update/lifecycle hook, so on those agents re-run +`/magpie-setup upgrade` yourself after updating the package. + ## Versioning The plugin version tracks the framework release (`0.2.0`). The manifest diff --git a/hooks/check-upgrade.sh b/hooks/check-upgrade.sh new file mode 100755 index 000000000..db7823580 --- /dev/null +++ b/hooks/check-upgrade.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: Apache-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Claude Code SessionStart hook for the Apache Magpie plugin. +# +# Detects when the installed plugin version has changed since the last +# session (i.e. the marketplace updated it) and prompts the user to run +# `/magpie-setup upgrade`, which reconciles the gitignored snapshot, the +# agentic overrides, and drift. +# +# Deliberately DETECT-AND-PROMPT, not auto-run: a Claude Code hook cannot +# invoke a slash command, and Magpie never mutates an adopter repo without +# the guided skill's confirmation. The hook is read-only apart from writing +# its own version marker; it makes no network calls and touches nothing in +# the adopter repo. +set -euo pipefail + +# Drain the event JSON delivered on stdin (unused). +cat >/dev/null 2>&1 || true + +root="${CLAUDE_PLUGIN_ROOT:-.}" +data="${CLAUDE_PLUGIN_DATA:-$root}" +manifest="$root/.claude-plugin/plugin.json" + +[ -f "$manifest" ] || exit 0 + +# Extract "version" without a jq dependency. +current="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$manifest" | head -n1)" +[ -n "$current" ] || exit 0 + +marker="$data/magpie-plugin-version" +stored="" +[ -f "$marker" ] && stored="$(cat "$marker" 2>/dev/null || true)" + +if [ "$current" != "$stored" ]; then + mkdir -p "$data" 2>/dev/null || true + printf '%s' "$current" >"$marker" 2>/dev/null || true + + if [ -n "$stored" ]; then + echo "Apache Magpie plugin updated ($stored -> $current). If this repo adopts Magpie, run \`/magpie-setup upgrade\` to reconcile the snapshot, agentic overrides, and drift." >&2 + else + echo "Apache Magpie plugin $current is active. If this repo already adopts Magpie, run \`/magpie-setup upgrade\` to reconcile; otherwise run \`/magpie-setup\` to adopt." >&2 + fi +fi + +exit 0 From 1ecaade4377e65b18ffdd4ad64461ef107db6f19 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 05:30:27 +0200 Subject: [PATCH 03/14] feat(marketplace): prompt Codex and Gemini users to run upgrade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the update-detection prompt beyond Claude Code: - Make hooks/check-upgrade.sh agent-neutral (resolves CLAUDE_/CODEX_ plugin root+data env vars; reads version from whichever plugin manifest is present). - Codex CLI: wire the same script via the plugin's hooks block (schema best-effort, flagged for verification before publish). - Gemini CLI: ship GEMINI.md extension context (contextFileName) instructing the agent to compare the extension version to a recorded marker and prompt for /magpie-setup upgrade on change — Gemini has no lifecycle hook. Documented per-agent in docs/setup/marketplaces.md. --- .codex-plugin/plugin.json | 15 ++++++++++++++- GEMINI.md | 38 +++++++++++++++++++++++++++++++++++++ docs/setup/marketplaces.md | 39 +++++++++++++++++++------------------- gemini-extension.json | 3 ++- hooks/check-upgrade.sh | 32 +++++++++++++++++++------------ 5 files changed, 93 insertions(+), 34 deletions(-) create mode 100644 GEMINI.md diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 48229064a..ac6a827cc 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -2,5 +2,18 @@ "name": "magpie", "version": "0.2.0", "description": "Apache Magpie — agentic skills for maintaining open-source projects.", - "skills": "./skills" + "skills": "./skills", + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "${CODEX_PLUGIN_ROOT}/hooks/check-upgrade.sh", + "timeout": 10 + } + ] + } + ] + } } diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 000000000..af5373d2c --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,38 @@ + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Apache Magpie — Gemini CLI extension context](#apache-magpie--gemini-cli-extension-context) + - [Upgrade prompt](#upgrade-prompt) + + + + + +# Apache Magpie — Gemini CLI extension context + +This context ships with the Apache Magpie Gemini CLI extension. Gemini has no +plugin update/lifecycle hook (unlike Claude Code), so this file is how the +extension prompts you to reconcile after an update. + +## Upgrade prompt + +The Magpie extension declares its version in `gemini-extension.json`. At the +start of a session, compare that `version` against the value you last recorded +in `magpie-plugin-version` under the extension's state directory (create the +file if it does not exist): + +- If the version has **changed** since you last recorded it — the marketplace + updated the extension — tell the user, once and non-intrusively: + + > Apache Magpie was updated to ``. If this repo adopts Magpie, run + > `/magpie-setup upgrade` to reconcile the snapshot, agentic overrides, and + > drift. + + Then record the new version. + +- If the version is unchanged, say nothing. + +Never run the upgrade yourself: `/magpie-setup upgrade` is a guided, +user-confirmed action, and Magpie never mutates an adopter repo unattended. diff --git a/docs/setup/marketplaces.md b/docs/setup/marketplaces.md index dfaf66620..0f04317fa 100644 --- a/docs/setup/marketplaces.md +++ b/docs/setup/marketplaces.md @@ -8,7 +8,7 @@ - [Installing Apache Magpie from agent marketplaces](#installing-apache-magpie-from-agent-marketplaces) - [Supported agents](#supported-agents) - [Not supported](#not-supported) - - [Automatic upgrade detection (Claude Code)](#automatic-upgrade-detection-claude-code) + - [Automatic upgrade detection](#automatic-upgrade-detection) - [Versioning](#versioning) - [Verification status](#verification-status) @@ -66,25 +66,24 @@ with the installing agent's namespacing (e.g. `/magpie:release-vote-tally`). (MCP) servers, not `SKILL.md` skills. Distributing Magpie there would require wrapping skills behind an MCP server (a rebuild, not packaging). -## Automatic upgrade detection (Claude Code) - -The Claude Code plugin ships a `SessionStart` hook -([`hooks/check-upgrade.sh`](../../hooks/check-upgrade.sh)) that fires when a -session starts. It compares the installed plugin version against a marker in -the plugin's persistent data directory and, when the marketplace has updated -the plugin to a new version, prompts you to run **`/magpie-setup upgrade`** — -which reconciles the gitignored snapshot, the agentic overrides, and drift. - -The hook **detects and prompts**; it does not run the upgrade itself. Claude -Code hooks cannot invoke a slash command, and — by design — Magpie never -mutates an adopter repo without the guided skill's confirmation, so the -*trigger* is automatic while the *changes* stay confirmed. The hook is -read-only apart from writing its own version marker, makes no network calls, -and touches nothing in the adopter repo. - -This lifecycle wiring is **Claude Code-only**: Codex CLI and Gemini CLI have -no equivalent plugin update/lifecycle hook, so on those agents re-run -`/magpie-setup upgrade` yourself after updating the package. +## Automatic upgrade detection + +When the marketplace updates the plugin to a new version, Magpie prompts you +to run **`/magpie-setup upgrade`** — which reconciles the gitignored snapshot, +the agentic overrides, and drift. This is **detect-and-prompt, not auto-run**: +a plugin hook cannot invoke a slash command, and Magpie never mutates an +adopter repo without the guided skill's confirmation, so the *trigger* is +automatic while the *changes* stay confirmed. + +| Agent | Mechanism | +|---|---| +| **Claude Code** | `SessionStart` hook [`hooks/check-upgrade.sh`](../../hooks/check-upgrade.sh) compares the installed version to a marker in the plugin's persistent data dir and prompts on change. Deterministic. | +| **Codex CLI** | The same [`hooks/check-upgrade.sh`](../../hooks/check-upgrade.sh) wired via the plugin's `hooks` block. Codex's hook schema is **not yet verified** — confirm before publish. | +| **Gemini CLI** | No lifecycle hook; the extension context file [`GEMINI.md`](../../GEMINI.md) instructs the agent to compare the extension version to a recorded marker and prompt on change (LLM-driven, advisory). | +| Other agents | Re-run `/magpie-setup upgrade` manually after updating the package. | + +The hook script is read-only apart from writing its own version marker, makes +no network calls, and touches nothing in the adopter repo. ## Versioning diff --git a/gemini-extension.json b/gemini-extension.json index 116dd7b24..b93946885 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -1,5 +1,6 @@ { "name": "magpie", "version": "0.2.0", - "description": "Apache Magpie — agentic skills for maintaining open-source projects. Skills are auto-discovered from ./skills." + "description": "Apache Magpie — agentic skills for maintaining open-source projects. Skills are auto-discovered from ./skills.", + "contextFileName": "GEMINI.md" } diff --git a/hooks/check-upgrade.sh b/hooks/check-upgrade.sh index db7823580..f3256a722 100755 --- a/hooks/check-upgrade.sh +++ b/hooks/check-upgrade.sh @@ -2,30 +2,38 @@ # SPDX-License-Identifier: Apache-2.0 # https://www.apache.org/licenses/LICENSE-2.0 # -# Claude Code SessionStart hook for the Apache Magpie plugin. +# Session-start hook for the Apache Magpie plugin. Wired by the Claude Code +# plugin (SessionStart) and — best-effort, pending schema verification — by +# the Codex CLI plugin, where a session hook is available. # # Detects when the installed plugin version has changed since the last # session (i.e. the marketplace updated it) and prompts the user to run # `/magpie-setup upgrade`, which reconciles the gitignored snapshot, the # agentic overrides, and drift. # -# Deliberately DETECT-AND-PROMPT, not auto-run: a Claude Code hook cannot -# invoke a slash command, and Magpie never mutates an adopter repo without -# the guided skill's confirmation. The hook is read-only apart from writing -# its own version marker; it makes no network calls and touches nothing in -# the adopter repo. +# Deliberately DETECT-AND-PROMPT, not auto-run: a plugin hook cannot invoke +# a slash command, and Magpie never mutates an adopter repo without the +# guided skill's confirmation. The hook is read-only apart from writing its +# own version marker; it makes no network calls and touches nothing in the +# adopter repo. +# +# Agents without a session hook (e.g. Gemini CLI) surface the same prompt via +# their extension context file (GEMINI.md) instead. set -euo pipefail -# Drain the event JSON delivered on stdin (unused). +# Drain any event JSON delivered on stdin (unused). cat >/dev/null 2>&1 || true -root="${CLAUDE_PLUGIN_ROOT:-.}" -data="${CLAUDE_PLUGIN_DATA:-$root}" -manifest="$root/.claude-plugin/plugin.json" +root="${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-.}}" +data="${CLAUDE_PLUGIN_DATA:-${CODEX_PLUGIN_DATA:-$root/.magpie-state}}" -[ -f "$manifest" ] || exit 0 +# Read the plugin version from whichever manifest is present. +manifest="" +for m in "$root/.claude-plugin/plugin.json" "$root/.codex-plugin/plugin.json"; do + [ -f "$m" ] && { manifest="$m"; break; } +done +[ -n "$manifest" ] || exit 0 -# Extract "version" without a jq dependency. current="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$manifest" | head -n1)" [ -n "$current" ] || exit 0 From ebcfce1262ed2fc2215237e3e2f71e9bb2e328f3 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 16:14:10 +0200 Subject: [PATCH 04/14] docs(readme): fold #901 README simplification; lead with install (adopt alias) Folds the top-level README simplification from #901 (by @meonkeys) into the 0.2.0 marketplace work, and shifts the primary term to 'install' now that marketplace installation exists: - Keeps Adam's simplified Install / Usage / Update / Skill-families structure. - Install section covers both paths: agent marketplace (easiest, nothing committed) and the committed snapshot via /magpie-setup. - 'install' is the primary verb/command; '/magpie-setup adopt' stays an alias; 'Adopt a Magpie' remains the landing-page slogan; 'adopter'/'adopter repo' kept as the ecosystem noun. - Repoints inbound doc links from the removed README#adopting-the-framework anchor to README#install and updates their labels. Supersedes the standalone #901 (its Fix #899 goal already merged via #905). Credit to @meonkeys for the simplification. --- README.md | 215 +++++++++----------------------- docs/index.md | 4 +- docs/issue-management/README.md | 2 +- docs/pr-management/README.md | 2 +- docs/security/README.md | 2 +- docs/setup/README.md | 2 +- docs/setup/unadopt.md | 2 +- 7 files changed, 67 insertions(+), 162 deletions(-) diff --git a/README.md b/README.md index ca6481611..854ebe084 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,11 @@ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [Apache Magpie](#apache-magpie) - - [How adoption works](#how-adoption-works) - - [Adopting the framework](#adopting-the-framework) - - [1. Bootstrap (copy-pasteable shell)](#1-bootstrap-copy-pasteable-shell) - - [2. Skill takeover](#2-skill-takeover) - - [Subsequent contributors](#subsequent-contributors) - - [Drift detection](#drift-detection) + - [Install](#install) + - [Usage](#usage) + - [Update / maintain](#update--maintain) - [Skill families](#skill-families) - [External skill sources](#external-skill-sources) - - [Maintenance](#maintenance) - [Acknowledgements](#acknowledgements) - [Cross-references](#cross-references) @@ -27,155 +23,77 @@ [![Magpie](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/apache/magpie/main/assets/badge.json)](https://magpie.apache.org/) -Apache Magpie provides high-quality recipes for agent-assisted software project -maintenance. +**Adopt a Magpie.** Apache Magpie provides high-quality recipes for +agent-assisted software project maintenance. These recipes allow human maintainers working with AIs to efficiently handle -the repetitive parts of running an open-source project: Issue triage, PR +the repetitive parts of running an open-source project: issue triage, PR review, mentoring contributors, managing security reports, and more. -Magpie is distributed per the [ASF release policy](https://www.apache.org/legal/release-policy.html) -(see [release-distribution](https://infra.apache.org/release-distribution.html) -for the canonical mechanism). Two ways in: adopt a release with -`/magpie-setup` (full snapshot + override adoption), or install the skills -directly from your agent's plugin marketplace — Claude Code, Codex, Copilot, -Gemini, Cursor, and more. See -[`docs/setup/marketplaces.md`](docs/setup/marketplaces.md). +Magpie is currently in development for ASF projects + Python Core team +friendlies. Testers welcome! > [!IMPORTANT] > The motivation, scope, and design commitments behind this work > live in [`MISSION.md`](MISSION.md) — the founding mission of the > Apache Magpie Top-Level Project, originally filed as its > establishment proposal. Read that for the *why*; this README is -> the *how* once you've decided to adopt. - -## How adoption works - -The framework uses a **snapshot + agentic-override** adoption -model. An adopter project commits a single skill — -[`setup`](skills/setup/SKILL.md) — -into their repo. That skill manages everything else: - -1. **Snapshot.** `setup` downloads the framework into - a **gitignored** `/.apache-magpie/` directory. - The snapshot is a build artefact, not source — refreshed - by `/magpie-setup upgrade`, never committed. -2. **Symlinks.** `setup` symlinks the framework's - skills (security, pr-management, the rest of setup) under - one canonical home — `.agents/skills/` (the path shared by - Codex, Cursor, Gemini CLI, Copilot, …) — and gives every - other agent dir (`.claude/skills/`, `.github/skills/`, …) a - thin per-skill **relay** symlink pointing back at the - canonical entry. This is the same regardless of how the - adopter previously organised those dirs. The symlinks are - **also gitignored** — they ultimately target the gitignored - snapshot, so they would dangle on a fresh clone before - `/magpie-setup` runs. -3. **Overrides.** Adopter-specific modifications to framework - workflows live as agent-readable markdown under - `/.apache-magpie-overrides/.md`, - **committed** in the adopter repo. The framework's skills - consult those files at run-time and apply the overrides - before executing default behaviour. See - [`docs/setup/agentic-overrides.md`](docs/setup/agentic-overrides.md) - for the contract. - -**No git submodules. No vendored copies of framework skills.** Just one -committed skill (the bootstrap), a gitignored snapshot, and agent-readable -override files. (Prefer a marketplace? See -[`docs/setup/marketplaces.md`](docs/setup/marketplaces.md).) - -## Adopting the framework - -Two phases — a **shell bootstrap** that gets `setup` -into your repo, then the **skill takeover** that wires up the -rest interactively. - -### 1. Bootstrap (copy-pasteable shell) - -Pick an install method and follow the verbatim recipe in -[**`docs/setup/install-recipes.md`**](docs/setup/install-recipes.md): - -| Method | When to use | Reproducibility | -|---|---|---| -| `svn-zip` | Production once ASF official releases ship to `dist.apache.org` (signed + checksummed) | Frozen by version | -| `git-tag` | Pin a specific framework version | Frozen by tag | -| `git-branch` (default `main`) | WIP path — track the framework's `main` directly for the latest unreleased changes. | Tracks tip | - -Each recipe is a single shell block that: - -1. Adds `.apache-magpie/`, `.apache-magpie.local.lock`, and - the framework-skill symlinks to `.gitignore`. -2. Downloads + verifies + extracts the framework into - `.apache-magpie/` (gitignored — build artefact, not - source). -3. Copies the - [`setup`](skills/setup/SKILL.md) - skill into the canonical `.agents/skills/magpie-setup/` and - adds a relay symlink to it from each agent dir you use - (`.claude/skills/magpie-setup`, `.github/skills/magpie-setup`). - -After the recipe completes, the framework snapshot is on -disk and the bootstrap skill is in your repo. - -### 2. Skill takeover - -Tell your agent: **"adopt apache/magpie in my repo"** -(or invoke `/magpie-setup` directly). The skill walks -through the rest: - -- writes `.apache-magpie.lock` (committed) — the project's - pin: install method + URL + ref + verification anchor; -- writes `.apache-magpie.local.lock` (gitignored) — what - this machine actually fetched + when; -- asks which skill families (`security`, `pr-management`) to - symlink in; -- creates the gitignored framework-skill symlinks; -- scaffolds `.apache-magpie-overrides/` (committed) for any - local workflow modifications; -- installs a `post-checkout` git hook so worktrees re-create - runtime state automatically; -- updates your project documentation with a brief mention. - -After the skill finishes, you commit the small, focused -diff — the bootstrap skill, the `.gitignore` entries, the -two lock files (committed + gitignore exclusion for the -local one), the overrides scaffold, the doc note — and you're -done. Open a PR. - -### Subsequent contributors - -Future contributors who clone your repo just say "adopt -Magpie in this repo" (or invoke `/magpie-setup`). -The skill reads `.apache-magpie.lock` (already committed) -and re-installs to the same version your project pinned. No -need to redo the manual recipe — the committed lock is the -project's source-of-truth. - -### Drift detection - -Every framework skill compares the gitignored -`.apache-magpie.local.lock` against the committed -`.apache-magpie.lock` at the top of its run. If they have -drifted (project lead bumped the pin, or the local install -is stale on a `main`-tracking adopter), the skill surfaces -the gap and proposes `/magpie-setup upgrade`. `upgrade` -deletes the gitignored snapshot, re-installs per the -committed pin, refreshes the gitignored symlinks, and -reconciles any agentic overrides — see -[`docs/setup/install-recipes.md`](docs/setup/install-recipes.md) -and -[`skills/setup/upgrade.md`](skills/setup/upgrade.md) -for the full flow. +> the *how* once you've decided to install. + +## Install + +Magpie's recipes are plain-text instructions followed by an AI agent you +control. There are two ways to install them: + +**From an agent marketplace (easiest).** Install the skills directly into your +agent — Claude Code, Codex, Copilot, Gemini, Cursor, and more — with nothing +committed to your repository. See +[`docs/setup/marketplaces.md`](docs/setup/marketplaces.md). + +**As a committed snapshot (full install into your repo).** For a project that +installs Magpie as part of its own source — pin a release and let +`/magpie-setup` set up the snapshot, overrides, and drift detection in the +adopter repo: + +1. [Download / pin a release](https://magpie.apache.org/downloads/) +2. Set up the symlinks and git-ignores — see + [`docs/setup/install-recipes.md`](docs/setup/install-recipes.md) +3. Ask your agent to complete the install: `/magpie-setup install` + (`/magpie-setup adopt` is an alias) + +## Usage + +Magpie is used by interacting with your AI agents. You'll use plain-language +prompts like + +> review PR #5193 + +or + +> triage the latest security reports + +or skill calls starting with a slash, like + +> /dependency-audit + +## Update / maintain + +- `/magpie-setup upgrade` — refresh the snapshot to a newer + framework version + reconcile any overrides against the new + framework structure. +- `/magpie-setup verify` — read-only health check (snapshot + intact, symlinks live, `.gitignore` correct, etc.). +- `/magpie-setup override ` — open or + scaffold an override file for a framework skill. ## Skill families -Ten skill families ship in the framework, all at `experimental` or +The following skill families ship in the framework, all at `experimental` or `stable`, and each skill declares its family in a `family:` frontmatter -key. At adoption (and on every upgrade), `/magpie-setup` offers the +key. At install (and on every upgrade), `/magpie-setup` offers the **opt-in** families — and the optional **MCP servers** (`ponymail`, `apache-projects`, `gmail-plaintext`) — in a single install choice; -symlinks for the picked families land in the adopter's skill directory. +symlinks for the picked families land in the skill directory. The two **always-on** families (`setup`, `utilities`) are wired unconditionally and never prompted for. @@ -185,7 +103,7 @@ means and which modes are still proposed vs. shipping today. | Family | Type | Modes | Purpose | Detail | |---|---|---|---|---| -| [**setup**](docs/setup/README.md) | always-on | (infra) | Isolated agent setup, framework adoption + maintenance, shared-config sync. The prerequisite — at minimum the `setup` skill itself runs out of this family. | 9 skills, [`docs/setup/`](docs/setup/) | +| [**setup**](docs/setup/README.md) | always-on | (infra) | Isolated agent setup, framework install + maintenance, shared-config sync. The prerequisite — at minimum the `setup` skill itself runs out of this family. | 9 skills, [`docs/setup/`](docs/setup/) | | **utilities** | always-on | (meta) | Framework meta-skills: author skills (`write-skill`), restructure them (`optimize-skill`), reconcile skill state (`skill-reconciler`), and print a live index (`list-skills`). | 4 skills | | [**security**](docs/security/README.md) | opt-in | Triage, Drafting | 16-step security-issue handling lifecycle — from `security@` import through CVE publication, including state sync. Maintainer-only. | 12 skills, [`docs/security/`](docs/security/) | | [**pr-management**](docs/pr-management/README.md) | opt-in | Triage | Maintainer-facing PR-queue management — triage, stats, deep code review, express-lane merge, stale-sweep, reviewer routing, and pre-first-PR checks. | 8 skills, [`docs/pr-management/`](docs/pr-management/README.md) | @@ -198,7 +116,7 @@ means and which modes are still proposed vs. shipping today. ### External skill sources -Beyond the in-tree families, an adopter can pull a skill or whole family +Skill families or individual skills can be pulled from a **trusted external source** — a repo other than `apache/magpie` that ships Magpie-shaped skills (with their evals and tests). Where a skill directory would sit, a `skills//source.md` **redirect** names a @@ -209,19 +127,6 @@ skill. Nothing is fetched unless the adopter commits the pin — see [`PRINCIPLES.md` §13](PRINCIPLES.md#13-snapshot-plus-override-never-vendored-copies), and [`RFC-AI-0006`](docs/rfcs/RFC-AI-0006.md). -## Maintenance - -After the initial adoption, the same skill handles ongoing -maintenance: - -- `/magpie-setup upgrade` — refresh the snapshot to a newer - framework version + reconcile any overrides against the new - framework structure. -- `/magpie-setup verify` — read-only health check (snapshot - intact, symlinks live, `.gitignore` correct, etc.). -- `/magpie-setup override ` — open or - scaffold an override file for a framework skill. - ## Acknowledgements Apache Magpie was first developed and proven inside **Apache Airflow**, and was diff --git a/docs/index.md b/docs/index.md index c4a85e3e1..f47ad5d3f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -78,7 +78,7 @@ Start with [`setup`](setup/README.md) regardless — it is the prerequisite ever You have an open-source project with an issue tracker and/or PR queue, and you want agent assistance with the mechanical parts. -→ Start with the [README](../README.md#adopting-the-framework) (adoption steps) and [install recipes](setup/install-recipes.md). +→ Start with the [README](../README.md#install) (install steps) and [install recipes](setup/install-recipes.md). ### Security team members @@ -128,7 +128,7 @@ from mailing lists, slack etc. | Pull a skill/family from a trusted external source | [skill-sources/README.md](skill-sources/README.md) | | Extend Magpie (project / org / individual) | [extending.md](extending.md) | | See what skills exist today | [modes.md](modes.md) | -| Adopt in my project | [README → Adopting](../README.md#adopting-the-framework) | +| Install in my project | [README → Install](../README.md#install) | | Set up the secure agent sandbox | [setup/](setup/README.md) | | Understand the security workflow | [security/](security/README.md) | | Know what it costs to run | [mode-economics.md](mode-economics.md) | diff --git a/docs/issue-management/README.md b/docs/issue-management/README.md index 7283ea5c6..0a91550da 100644 --- a/docs/issue-management/README.md +++ b/docs/issue-management/README.md @@ -128,7 +128,7 @@ uv run --project tools/pilot-report-validator pilot-report-validate /` layout, ## Cross-references -- [Top-level README — Adopting the framework](../../README.md#adopting-the-framework) — 3-step bootstrap. +- [Top-level README — Install](../../README.md#install) — 3-step bootstrap. - [`projects/_template/README.md`](../../projects/_template/README.md) — adopter scaffold index, including the PR-management config files. - [`tools/spec-loop/specs/pr-management-family.md`](../../tools/spec-loop/specs/pr-management-family.md) — functional spec: acceptance criteria, validation commands, and known gaps. - [`docs/mentoring/README.md`](../mentoring/README.md) — `pr-management-mentor` family overview. diff --git a/docs/security/README.md b/docs/security/README.md index acd2ae8b1..cf1345f43 100644 --- a/docs/security/README.md +++ b/docs/security/README.md @@ -113,7 +113,7 @@ Optional but commonly needed: ## Cross-references -- [Top-level README — Adopting the framework](../../README.md#adopting-the-framework) — 3-step bootstrap. +- [Top-level README — Install](../../README.md#install) — 3-step bootstrap. - [`docs/prerequisites.md`](../prerequisites.md) — what a security triager / remediation developer / release manager needs installed before invoking any skill. diff --git a/docs/setup/README.md b/docs/setup/README.md index 66e691964..a194d08ee 100644 --- a/docs/setup/README.md +++ b/docs/setup/README.md @@ -107,6 +107,6 @@ file under `~/.claude-config/`). ## Cross-references -- [Top-level README — Adopting the framework](../../README.md#adopting-the-framework) — 3-step bootstrap. +- [Top-level README — Install](../../README.md#install) — 3-step bootstrap. - [`docs/prerequisites.md`](../prerequisites.md) — what each framework skill needs (Claude Code, Gmail MCP, GitHub auth, browser, etc.). diff --git a/docs/setup/unadopt.md b/docs/setup/unadopt.md index 6e8ff62dd..0443643c1 100644 --- a/docs/setup/unadopt.md +++ b/docs/setup/unadopt.md @@ -237,7 +237,7 @@ a first-time adopter takes. - [`docs/setup/README.md`](README.md) — the setup skill family overview (verify, upgrade, shared-config sync). -- [Top-level README — Adopting the framework](../../README.md#adopting-the-framework) +- [Top-level README — Install](../../README.md#install) — the original 3-step bootstrap. - [`install-recipes.md`](install-recipes.md) — the counterpart to this page. From ed07163f0041d075fe8a8aaf44ccf09262eebd33 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 16:33:27 +0200 Subject: [PATCH 05/14] docs(readme): project adopts Magpie; installation is the mechanism Keep 'adopt' for the project relationship and 'install/installation' for the process, per the terminology model: a project adopts Magpie into its source; /magpie-setup installs the snapshot. --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 854ebe084..813acbdf5 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,9 @@ agent — Claude Code, Codex, Copilot, Gemini, Cursor, and more — with nothing committed to your repository. See [`docs/setup/marketplaces.md`](docs/setup/marketplaces.md). -**As a committed snapshot (full install into your repo).** For a project that -installs Magpie as part of its own source — pin a release and let -`/magpie-setup` set up the snapshot, overrides, and drift detection in the -adopter repo: +**As a committed snapshot (installed into your repo).** For a project that +adopts Magpie into its own source — pin a release and let `/magpie-setup` +install the snapshot, overrides, and drift detection in the adopter repo: 1. [Download / pin a release](https://magpie.apache.org/downloads/) 2. Set up the symlinks and git-ignores — see From d7cd56a8da6a57c6ca2e350080f1667651a66f6f Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 16:39:24 +0200 Subject: [PATCH 06/14] docs(readme): explain adopt (relationship) vs install (process) in the Install section --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 813acbdf5..0d3adfe7e 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,9 @@ friendlies. Testers welcome! ## Install -Magpie's recipes are plain-text instructions followed by an AI agent you -control. There are two ways to install them: +You **adopt** Magpie once — the decision to bring the framework into your +project — while **installation** is how you carry that out. There are two +ways to install: **From an agent marketplace (easiest).** Install the skills directly into your agent — Claude Code, Codex, Copilot, Gemini, Cursor, and more — with nothing From 879add4b1298b32382277f28f34941fef666a38d Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 16:45:24 +0200 Subject: [PATCH 07/14] docs(marketplaces): detailed per-agent install steps from the GitHub marketplace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expand docs/setup/marketplaces.md from a one-line-per-agent table into step-by-step instructions for installing Magpie from the apache/magpie GitHub repo-as-marketplace: add marketplace, install, verify, invoke, update — for Claude Code, Codex, Copilot, Gemini, Cursor, apm, Kiro, OpenCode. States plainly that the GitHub repo is the marketplace (no vendor official directory needed) and how to pin a tag vs track main. Verify-flagged the still-evolving CLIs (Codex/Copilot/apm/Cursor). --- docs/setup/marketplaces.md | 169 +++++++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 7 deletions(-) diff --git a/docs/setup/marketplaces.md b/docs/setup/marketplaces.md index 0f04317fa..6b418d676 100644 --- a/docs/setup/marketplaces.md +++ b/docs/setup/marketplaces.md @@ -7,6 +7,14 @@ - [Installing Apache Magpie from agent marketplaces](#installing-apache-magpie-from-agent-marketplaces) - [Supported agents](#supported-agents) + - [Claude Code](#claude-code) + - [OpenAI Codex CLI](#openai-codex-cli) + - [GitHub Copilot](#github-copilot) + - [Google Gemini CLI](#google-gemini-cli) + - [Cursor](#cursor) + - [microsoft/apm (multiplexer)](#microsoftapm-multiplexer) + - [Kiro (AWS)](#kiro-aws) + - [OpenCode](#opencode) - [Not supported](#not-supported) - [Automatic upgrade detection](#automatic-upgrade-detection) - [Versioning](#versioning) @@ -46,16 +54,163 @@ with the installing agent's namespacing (e.g. `/magpie:release-vote-tally`). ## Supported agents -| Agent | Install | Manifest in this repo | +Every method below uses the **GitHub repository +[`apache/magpie`](https://github.com/apache/magpie)** as the marketplace — +no third-party or vendor "official" directory is required. Pin to a released +tag (e.g. `0.2.0`) for reproducibility, or track `main` for the latest. + +Quick reference: + +| Agent | One-liner | Manifest in this repo | |---|---|---| -| **Claude Code** | `/plugin marketplace add apache/magpie` then `/plugin install magpie@apache-magpie` | `.claude-plugin/marketplace.json`, `.claude-plugin/plugin.json` | -| **OpenAI Codex CLI** | `codex plugin marketplace add apache/magpie` then install `magpie` | `.codex-plugin/plugin.json`, `.agents/plugins/marketplace.json` | -| **GitHub Copilot** | add the repo as a plugin marketplace, then install `magpie` | `marketplace.json` (repo root) | +| **Claude Code** | `/plugin marketplace add apache/magpie` → `/plugin install magpie@apache-magpie` | `.claude-plugin/marketplace.json` + `plugin.json` | +| **OpenAI Codex CLI** | `codex plugin marketplace add apache/magpie` → install `magpie` | `.codex-plugin/plugin.json`, `.agents/plugins/marketplace.json` | +| **GitHub Copilot** | add `apache/magpie` as a plugin marketplace → install `magpie` | `marketplace.json` (repo root) | | **Google Gemini CLI** | `gemini extensions install https://github.com/apache/magpie` | `gemini-extension.json` | -| **Cursor** | add via the plugin/skill install flow pointing at the repo | consumes the plugin manifests above | +| **Cursor** | add via the plugin/skill install flow pointing at the repo | the plugin manifests above | | **microsoft/apm** | `apm install apache/magpie` (compiles to Claude/Cursor/Codex/Copilot/Gemini) | `apm.yml` | -| **Kiro** | install from the GitHub subdirectory of a skill (Kiro installs per-skill from a subdir, not the repo root) | native `skills//SKILL.md` | -| **OpenCode** | drop the skills into `.opencode/skills/`, or use a community installer against this repo | native `skills//SKILL.md` | +| **Kiro** | install per-skill from a GitHub subdirectory | native `skills//SKILL.md` | +| **OpenCode** | clone skills into `.opencode/skills/`, or use a community installer | native `skills//SKILL.md` | + +Detailed steps per agent follow. + +### Claude Code + +1. In a Claude Code session, add the marketplace from GitHub — this clones + the repo and reads `.claude-plugin/marketplace.json`: + + ```text + /plugin marketplace add apache/magpie + ``` + +2. Install the `magpie` plugin from it: + + ```text + /plugin install magpie@apache-magpie + ``` + +3. Confirm it is enabled (the `magpie` plugin should appear as installed): + + ```text + /plugin + ``` + +4. Invoke any skill under the plugin namespace, e.g.: + + ```text + /magpie:release-vote-tally + /magpie:security-issue-triage + ``` + +5. **Update** later with `/plugin marketplace update apache-magpie` then + `/plugin update magpie@apache-magpie`. On a version change the bundled + `SessionStart` hook also prompts you to run `/magpie-setup upgrade`. + +To pin a specific version instead of tracking `main`, add the marketplace +from the tag: `/plugin marketplace add apache/magpie@0.2.0`. + +### OpenAI Codex CLI + +1. Add the marketplace (reads `.agents/plugins/marketplace.json`): + + ```bash + codex plugin marketplace add apache/magpie + ``` + +2. Install the plugin: + + ```bash + codex plugin install magpie + ``` + +3. List / verify — inside Codex run `/plugins`, or from the shell + `codex plugin list`. + +> Codex's plugin/marketplace verbs are still evolving. If a command name +> differs, check `codex plugin --help`. + +### GitHub Copilot + +Copilot treats a GitHub repo with a root `marketplace.json` as a plugin +marketplace. + +1. Add `apache/magpie` as a plugin marketplace in Copilot (CLI or the + coding-agent settings). +2. Install the `magpie` plugin from it. +3. The skills become available to the agent under the plugin. + +> Copilot's plugin-marketplace commands are still stabilising (enterprise- +> managed plugins are in preview). Confirm the exact `add` / `install` +> syntax in the current Copilot documentation. + +### Google Gemini CLI + +1. Install the extension straight from GitHub (reads `gemini-extension.json` + and auto-discovers the skills under `skills/`): + + ```bash + gemini extensions install https://github.com/apache/magpie + ``` + +2. Verify: + + ```bash + gemini extensions list + ``` + +3. Use the skills by asking the agent in natural language or by skill name. + +4. **Update** with `gemini extensions update magpie`. Gemini has no lifecycle + hook, so the shipped [`GEMINI.md`](../../GEMINI.md) reminds you to run + `/magpie-setup upgrade` when the version changes. + +### Cursor + +Cursor consumes the same plugin/skill manifests. Add Magpie through Cursor's +plugin/skill install flow (Customize → Plugins/Skills) pointing at +`github.com/apache/magpie`. + +> Confirm the exact add flow in Cursor's current docs — its self-serve +> marketplace surface is evolving. + +### microsoft/apm (multiplexer) + +`apm` compiles one package to several agents at once (Claude, Cursor, Codex, +Copilot, Gemini). + +1. From your project root: + + ```bash + apm install apache/magpie + ``` + + (reads `apm.yml`, `type: skill`). + +2. `apm` deploys the skills into each supported agent's directory and writes + an `apm.lock.yaml` — commit it to pin the exact resolved commit. + +> `apm` schema is **v0.1** and may change; verify verbs with `apm --help`. + +### Kiro (AWS) + +Kiro installs skills **per-skill from a GitHub subdirectory** (it does not +consume the repo root). For each skill you want, point Kiro's "install from +GitHub" at that skill's subdir on a pinned tag, e.g.: + +```text +https://github.com/apache/magpie/tree/0.2.0/skills/release-vote-tally +``` + +Kiro reads the `skills//SKILL.md` there. + +### OpenCode + +OpenCode reads native Agent Skills from `.opencode/skills/`. Either: + +- clone the skill directories you want into `.opencode/skills/` (project) or + `~/.opencode/skills/` (personal) from `github.com/apache/magpie`, or +- use a community installer (e.g. the `opencode-skills-collection` npm + package) pointed at this repo. ### Not supported From 86846f63969e330124ad27fae33968dd695232bd Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 17:01:32 +0200 Subject: [PATCH 08/14] feat(marketplace): split into per-family plugins to cut always-on token cost Live-tested that Claude Code's plugin 'skills' field rejects paths outside the plugin dir, so per-family plugins reference the shared skills/ tree via single-hop symlinks (no copies, single source of truth). - Add 10 family plugins under plugins/magpie-/ (security, release- management, setup, pr-management, issue, repo-health, contributor-growth, utilities, mentoring, pairing), each a .claude-plugin/plugin.json + a skills/ dir of symlinks into ../../../skills/. - marketplace.json now lists the all-in-one 'magpie' plugin plus the 10 families. Family plugin.jsons omit version and inherit it from the marketplace entry (centralised versioning). - Verified via real installs: e.g. magpie-security = 12 skills / ~3.9k always-on, magpie-pairing = 2 / ~0.6k, vs the all-in-one 70 / ~21.7k. - Namespacing confirmed clean: /magpie-: (dir name, no magpie- frontmatter double-prefix). Docs: docs/setup/marketplaces.md gains the all-in-one-vs-per-family guidance and per-family token costs. --- .claude-plugin/marketplace.json | 69 ++++++++++++++++++- docs/setup/marketplaces.md | 42 +++++++++-- .../.claude-plugin/plugin.json | 5 ++ .../skills/committer-onboarding | 1 + .../skills/contributor-activity-sweep | 1 + .../skills/contributor-nomination | 1 + .../skills/contributor-sentiment | 1 + .../skills/contributor-to-committer | 1 + .../skills/onboarding-concierge | 1 + .../magpie-issue/.claude-plugin/plugin.json | 5 ++ .../magpie-issue/skills/issue-backlog-stats | 1 + plugins/magpie-issue/skills/issue-deduplicate | 1 + .../magpie-issue/skills/issue-fix-workflow | 1 + plugins/magpie-issue/skills/issue-reassess | 1 + .../magpie-issue/skills/issue-reassess-stats | 1 + plugins/magpie-issue/skills/issue-reproducer | 1 + plugins/magpie-issue/skills/issue-stale-sweep | 1 + plugins/magpie-issue/skills/issue-triage | 1 + .../.claude-plugin/plugin.json | 5 ++ .../skills/good-first-issue-author | 1 + .../skills/good-first-issue-sweep | 1 + .../magpie-mentoring/skills/mentoring-welcome | 1 + .../skills/newcomer-issue-explainer | 1 + .../magpie-pairing/.claude-plugin/plugin.json | 5 ++ .../skills/pairing-multi-agent-review | 1 + .../magpie-pairing/skills/pairing-self-review | 1 + .../.claude-plugin/plugin.json | 5 ++ .../skills/pr-management-code-review | 1 + .../skills/pr-management-mentor | 1 + .../skills/pr-management-quick-merge | 1 + .../skills/pr-management-stats | 1 + .../skills/pr-management-triage | 1 + .../skills/pr-stale-sweep | 1 + .../skills/pre-first-pr-check | 1 + .../skills/reviewer-routing | 1 + .../.claude-plugin/plugin.json | 5 ++ .../skills/release-announce-draft | 1 + .../skills/release-archive-sweep | 1 + .../skills/release-audit-report | 1 + .../skills/release-keys-sync | 1 + .../skills/release-prepare | 1 + .../skills/release-promote | 1 + .../skills/release-rc-cut | 1 + .../skills/release-verify-rc | 1 + .../skills/release-vote-draft | 1 + .../skills/release-vote-tally | 1 + .../.claude-plugin/plugin.json | 5 ++ .../skills/audit-finding-fix | 1 + .../magpie-repo-health/skills/ci-runner-audit | 1 + .../skills/dependency-audit | 1 + .../skills/dependency-license-audit | 1 + .../skills/flaky-test-triage | 1 + .../skills/license-compliance-audit | 1 + .../skills/workflow-security-audit | 1 + .../.claude-plugin/plugin.json | 5 ++ .../skills/security-cve-allocate | 1 + .../skills/security-issue-deduplicate | 1 + .../magpie-security/skills/security-issue-fix | 1 + .../skills/security-issue-import | 1 + .../skills/security-issue-import-from-md | 1 + .../skills/security-issue-import-from-pr | 1 + .../skills/security-issue-import-from-scan | 1 + .../security-issue-import-via-forwarder | 1 + .../skills/security-issue-invalidate | 1 + .../skills/security-issue-sync | 1 + .../skills/security-issue-triage | 1 + .../skills/security-tracker-stats-dashboard | 1 + .../magpie-setup/.claude-plugin/plugin.json | 5 ++ plugins/magpie-setup/skills/setup | 1 + .../skills/setup-isolated-setup-doctor | 1 + .../skills/setup-isolated-setup-install | 1 + .../skills/setup-isolated-setup-update | 1 + .../skills/setup-isolated-setup-verify | 1 + .../skills/setup-override-upstream | 1 + .../skills/setup-shared-config-sync | 1 + plugins/magpie-setup/skills/setup-status | 1 + .../magpie-setup/skills/setup-upstream-fix | 1 + .../.claude-plugin/plugin.json | 5 ++ plugins/magpie-utilities/skills/list-skills | 1 + .../magpie-utilities/skills/optimize-skill | 1 + .../magpie-utilities/skills/skill-reconciler | 1 + plugins/magpie-utilities/skills/write-skill | 1 + 82 files changed, 223 insertions(+), 8 deletions(-) create mode 100644 plugins/magpie-contributor-growth/.claude-plugin/plugin.json create mode 120000 plugins/magpie-contributor-growth/skills/committer-onboarding create mode 120000 plugins/magpie-contributor-growth/skills/contributor-activity-sweep create mode 120000 plugins/magpie-contributor-growth/skills/contributor-nomination create mode 120000 plugins/magpie-contributor-growth/skills/contributor-sentiment create mode 120000 plugins/magpie-contributor-growth/skills/contributor-to-committer create mode 120000 plugins/magpie-contributor-growth/skills/onboarding-concierge create mode 100644 plugins/magpie-issue/.claude-plugin/plugin.json create mode 120000 plugins/magpie-issue/skills/issue-backlog-stats create mode 120000 plugins/magpie-issue/skills/issue-deduplicate create mode 120000 plugins/magpie-issue/skills/issue-fix-workflow create mode 120000 plugins/magpie-issue/skills/issue-reassess create mode 120000 plugins/magpie-issue/skills/issue-reassess-stats create mode 120000 plugins/magpie-issue/skills/issue-reproducer create mode 120000 plugins/magpie-issue/skills/issue-stale-sweep create mode 120000 plugins/magpie-issue/skills/issue-triage create mode 100644 plugins/magpie-mentoring/.claude-plugin/plugin.json create mode 120000 plugins/magpie-mentoring/skills/good-first-issue-author create mode 120000 plugins/magpie-mentoring/skills/good-first-issue-sweep create mode 120000 plugins/magpie-mentoring/skills/mentoring-welcome create mode 120000 plugins/magpie-mentoring/skills/newcomer-issue-explainer create mode 100644 plugins/magpie-pairing/.claude-plugin/plugin.json create mode 120000 plugins/magpie-pairing/skills/pairing-multi-agent-review create mode 120000 plugins/magpie-pairing/skills/pairing-self-review create mode 100644 plugins/magpie-pr-management/.claude-plugin/plugin.json create mode 120000 plugins/magpie-pr-management/skills/pr-management-code-review create mode 120000 plugins/magpie-pr-management/skills/pr-management-mentor create mode 120000 plugins/magpie-pr-management/skills/pr-management-quick-merge create mode 120000 plugins/magpie-pr-management/skills/pr-management-stats create mode 120000 plugins/magpie-pr-management/skills/pr-management-triage create mode 120000 plugins/magpie-pr-management/skills/pr-stale-sweep create mode 120000 plugins/magpie-pr-management/skills/pre-first-pr-check create mode 120000 plugins/magpie-pr-management/skills/reviewer-routing create mode 100644 plugins/magpie-release-management/.claude-plugin/plugin.json create mode 120000 plugins/magpie-release-management/skills/release-announce-draft create mode 120000 plugins/magpie-release-management/skills/release-archive-sweep create mode 120000 plugins/magpie-release-management/skills/release-audit-report create mode 120000 plugins/magpie-release-management/skills/release-keys-sync create mode 120000 plugins/magpie-release-management/skills/release-prepare create mode 120000 plugins/magpie-release-management/skills/release-promote create mode 120000 plugins/magpie-release-management/skills/release-rc-cut create mode 120000 plugins/magpie-release-management/skills/release-verify-rc create mode 120000 plugins/magpie-release-management/skills/release-vote-draft create mode 120000 plugins/magpie-release-management/skills/release-vote-tally create mode 100644 plugins/magpie-repo-health/.claude-plugin/plugin.json create mode 120000 plugins/magpie-repo-health/skills/audit-finding-fix create mode 120000 plugins/magpie-repo-health/skills/ci-runner-audit create mode 120000 plugins/magpie-repo-health/skills/dependency-audit create mode 120000 plugins/magpie-repo-health/skills/dependency-license-audit create mode 120000 plugins/magpie-repo-health/skills/flaky-test-triage create mode 120000 plugins/magpie-repo-health/skills/license-compliance-audit create mode 120000 plugins/magpie-repo-health/skills/workflow-security-audit create mode 100644 plugins/magpie-security/.claude-plugin/plugin.json create mode 120000 plugins/magpie-security/skills/security-cve-allocate create mode 120000 plugins/magpie-security/skills/security-issue-deduplicate create mode 120000 plugins/magpie-security/skills/security-issue-fix create mode 120000 plugins/magpie-security/skills/security-issue-import create mode 120000 plugins/magpie-security/skills/security-issue-import-from-md create mode 120000 plugins/magpie-security/skills/security-issue-import-from-pr create mode 120000 plugins/magpie-security/skills/security-issue-import-from-scan create mode 120000 plugins/magpie-security/skills/security-issue-import-via-forwarder create mode 120000 plugins/magpie-security/skills/security-issue-invalidate create mode 120000 plugins/magpie-security/skills/security-issue-sync create mode 120000 plugins/magpie-security/skills/security-issue-triage create mode 120000 plugins/magpie-security/skills/security-tracker-stats-dashboard create mode 100644 plugins/magpie-setup/.claude-plugin/plugin.json create mode 120000 plugins/magpie-setup/skills/setup create mode 120000 plugins/magpie-setup/skills/setup-isolated-setup-doctor create mode 120000 plugins/magpie-setup/skills/setup-isolated-setup-install create mode 120000 plugins/magpie-setup/skills/setup-isolated-setup-update create mode 120000 plugins/magpie-setup/skills/setup-isolated-setup-verify create mode 120000 plugins/magpie-setup/skills/setup-override-upstream create mode 120000 plugins/magpie-setup/skills/setup-shared-config-sync create mode 120000 plugins/magpie-setup/skills/setup-status create mode 120000 plugins/magpie-setup/skills/setup-upstream-fix create mode 100644 plugins/magpie-utilities/.claude-plugin/plugin.json create mode 120000 plugins/magpie-utilities/skills/list-skills create mode 120000 plugins/magpie-utilities/skills/optimize-skill create mode 120000 plugins/magpie-utilities/skills/skill-reconciler create mode 120000 plugins/magpie-utilities/skills/write-skill diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 24b6940d7..c85b5d809 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,13 +1,76 @@ { "name": "apache-magpie", - "owner": { "name": "Apache Magpie", "url": "https://magpie.apache.org/" }, - "description": "Apache Magpie agentic skills for maintaining open-source projects.", + "owner": { + "name": "Apache Magpie", + "url": "https://magpie.apache.org/" + }, + "description": "Apache Magpie agentic skills for maintaining open-source projects. Install the all-in-one `magpie` plugin, or per-family plugins to load only what you use.", "plugins": [ { "name": "magpie", "source": ".", "version": "0.2.0", - "description": "All Apache Magpie skills (release management, security, PR/issue workflows, contributor growth, repo health)." + "description": "All Apache Magpie skills (all 10 families; ~21.7k always-on tokens)." + }, + { + "name": "magpie-contributor-growth", + "source": "./plugins/magpie-contributor-growth", + "version": "0.2.0", + "description": "Apache Magpie contributor-growth family (6 skills)." + }, + { + "name": "magpie-issue", + "source": "./plugins/magpie-issue", + "version": "0.2.0", + "description": "Apache Magpie issue family (8 skills)." + }, + { + "name": "magpie-mentoring", + "source": "./plugins/magpie-mentoring", + "version": "0.2.0", + "description": "Apache Magpie mentoring family (4 skills)." + }, + { + "name": "magpie-pairing", + "source": "./plugins/magpie-pairing", + "version": "0.2.0", + "description": "Apache Magpie pairing family (2 skills)." + }, + { + "name": "magpie-pr-management", + "source": "./plugins/magpie-pr-management", + "version": "0.2.0", + "description": "Apache Magpie pr-management family (8 skills)." + }, + { + "name": "magpie-release-management", + "source": "./plugins/magpie-release-management", + "version": "0.2.0", + "description": "Apache Magpie release-management family (10 skills)." + }, + { + "name": "magpie-repo-health", + "source": "./plugins/magpie-repo-health", + "version": "0.2.0", + "description": "Apache Magpie repo-health family (7 skills)." + }, + { + "name": "magpie-security", + "source": "./plugins/magpie-security", + "version": "0.2.0", + "description": "Apache Magpie security family (12 skills)." + }, + { + "name": "magpie-setup", + "source": "./plugins/magpie-setup", + "version": "0.2.0", + "description": "Apache Magpie setup family (9 skills)." + }, + { + "name": "magpie-utilities", + "source": "./plugins/magpie-utilities", + "version": "0.2.0", + "description": "Apache Magpie utilities family (4 skills)." } ] } diff --git a/docs/setup/marketplaces.md b/docs/setup/marketplaces.md index 6b418d676..085bb1aab 100644 --- a/docs/setup/marketplaces.md +++ b/docs/setup/marketplaces.md @@ -6,6 +6,7 @@ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [Installing Apache Magpie from agent marketplaces](#installing-apache-magpie-from-agent-marketplaces) + - [Choosing a plugin: all-in-one vs per-family](#choosing-a-plugin-all-in-one-vs-per-family) - [Supported agents](#supported-agents) - [Claude Code](#claude-code) - [OpenAI Codex CLI](#openai-codex-cli) @@ -48,9 +49,38 @@ through the plugin/extension mechanisms of the major AI coding agents, > `X.Y.Z` git tag; they are derived from — not a substitute for — the ASF > source release. -All entries package the whole framework as a single **`magpie`** plugin -that exposes every skill under the `skills/` tree. Skills are then invoked -with the installing agent's namespacing (e.g. `/magpie:release-vote-tally`). +## Choosing a plugin: all-in-one vs per-family + +The framework ships as **eleven** plugins so you install only what you use: + +- **`magpie`** (all-in-one) — every skill, all ten families. Simplest, but its + always-on metadata costs **~21.7k tokens in every session** (each installed + skill advertises a short description to the model on every turn — see + ["always-on" cost](#versioning) below). +- **Per-family plugins** — `magpie-security`, `magpie-release-management`, … — + install only the families you use, so only their skills are always-on. + **Recommended.** + +| Family plugin | Skills | ~Always-on tokens | +|---|---|---| +| `magpie-security` | 12 | ~3.9k | +| `magpie-release-management` | 10 | ~2.9k | +| `magpie-setup` | 9 | ~2.6k | +| `magpie-pr-management` | 8 | ~2.4k | +| `magpie-issue` | 8 | ~2.4k | +| `magpie-repo-health` | 7 | ~2.1k | +| `magpie-contributor-growth` | 6 | ~1.8k | +| `magpie-utilities` | 4 | ~1.4k | +| `magpie-mentoring` | 4 | ~1.2k | +| `magpie-pairing` | 2 | ~0.6k | +| **`magpie`** (all) | **70** | **~21.7k** | + +Skills are invoked under the installing plugin's namespace — e.g. +`/magpie:release-vote-tally` (all-in-one) or +`/magpie-release-management:release-vote-tally` (family plugin). + +Per-family plugins reference the shared `skills/` tree via symlinks (no copies), +so there is a single source of truth for every skill. ## Supported agents @@ -83,10 +113,12 @@ Detailed steps per agent follow. /plugin marketplace add apache/magpie ``` -2. Install the `magpie` plugin from it: +2. Install the all-in-one plugin, **or** just the families you use: ```text - /plugin install magpie@apache-magpie + /plugin install magpie@apache-magpie # everything (~21.7k always-on) + /plugin install magpie-security@apache-magpie # one family (~3.9k always-on) + /plugin install magpie-release-management@apache-magpie ``` 3. Confirm it is enabled (the `magpie` plugin should appear as installed): diff --git a/plugins/magpie-contributor-growth/.claude-plugin/plugin.json b/plugins/magpie-contributor-growth/.claude-plugin/plugin.json new file mode 100644 index 000000000..82e669b2a --- /dev/null +++ b/plugins/magpie-contributor-growth/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "magpie-contributor-growth", + "description": "Apache Magpie \u2014 path-to-committer: activity sweeps, nominations, sentiment, readiness, committer/post-vote onboarding.", + "skills": "./skills" +} diff --git a/plugins/magpie-contributor-growth/skills/committer-onboarding b/plugins/magpie-contributor-growth/skills/committer-onboarding new file mode 120000 index 000000000..b83c90500 --- /dev/null +++ b/plugins/magpie-contributor-growth/skills/committer-onboarding @@ -0,0 +1 @@ +../../../skills/committer-onboarding \ No newline at end of file diff --git a/plugins/magpie-contributor-growth/skills/contributor-activity-sweep b/plugins/magpie-contributor-growth/skills/contributor-activity-sweep new file mode 120000 index 000000000..fb487ce3e --- /dev/null +++ b/plugins/magpie-contributor-growth/skills/contributor-activity-sweep @@ -0,0 +1 @@ +../../../skills/contributor-activity-sweep \ No newline at end of file diff --git a/plugins/magpie-contributor-growth/skills/contributor-nomination b/plugins/magpie-contributor-growth/skills/contributor-nomination new file mode 120000 index 000000000..2124598e5 --- /dev/null +++ b/plugins/magpie-contributor-growth/skills/contributor-nomination @@ -0,0 +1 @@ +../../../skills/contributor-nomination \ No newline at end of file diff --git a/plugins/magpie-contributor-growth/skills/contributor-sentiment b/plugins/magpie-contributor-growth/skills/contributor-sentiment new file mode 120000 index 000000000..1831aa68c --- /dev/null +++ b/plugins/magpie-contributor-growth/skills/contributor-sentiment @@ -0,0 +1 @@ +../../../skills/contributor-sentiment \ No newline at end of file diff --git a/plugins/magpie-contributor-growth/skills/contributor-to-committer b/plugins/magpie-contributor-growth/skills/contributor-to-committer new file mode 120000 index 000000000..b8094a483 --- /dev/null +++ b/plugins/magpie-contributor-growth/skills/contributor-to-committer @@ -0,0 +1 @@ +../../../skills/contributor-to-committer \ No newline at end of file diff --git a/plugins/magpie-contributor-growth/skills/onboarding-concierge b/plugins/magpie-contributor-growth/skills/onboarding-concierge new file mode 120000 index 000000000..91029b53f --- /dev/null +++ b/plugins/magpie-contributor-growth/skills/onboarding-concierge @@ -0,0 +1 @@ +../../../skills/onboarding-concierge \ No newline at end of file diff --git a/plugins/magpie-issue/.claude-plugin/plugin.json b/plugins/magpie-issue/.claude-plugin/plugin.json new file mode 100644 index 000000000..ba22cc0ea --- /dev/null +++ b/plugins/magpie-issue/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "magpie-issue", + "description": "Apache Magpie \u2014 issue lifecycle: triage, reproduction, fix drafting, reassess, stale-sweep, dedup, backlog stats.", + "skills": "./skills" +} diff --git a/plugins/magpie-issue/skills/issue-backlog-stats b/plugins/magpie-issue/skills/issue-backlog-stats new file mode 120000 index 000000000..d12cad954 --- /dev/null +++ b/plugins/magpie-issue/skills/issue-backlog-stats @@ -0,0 +1 @@ +../../../skills/issue-backlog-stats \ No newline at end of file diff --git a/plugins/magpie-issue/skills/issue-deduplicate b/plugins/magpie-issue/skills/issue-deduplicate new file mode 120000 index 000000000..c6367f27b --- /dev/null +++ b/plugins/magpie-issue/skills/issue-deduplicate @@ -0,0 +1 @@ +../../../skills/issue-deduplicate \ No newline at end of file diff --git a/plugins/magpie-issue/skills/issue-fix-workflow b/plugins/magpie-issue/skills/issue-fix-workflow new file mode 120000 index 000000000..8914695dd --- /dev/null +++ b/plugins/magpie-issue/skills/issue-fix-workflow @@ -0,0 +1 @@ +../../../skills/issue-fix-workflow \ No newline at end of file diff --git a/plugins/magpie-issue/skills/issue-reassess b/plugins/magpie-issue/skills/issue-reassess new file mode 120000 index 000000000..c5f4026ee --- /dev/null +++ b/plugins/magpie-issue/skills/issue-reassess @@ -0,0 +1 @@ +../../../skills/issue-reassess \ No newline at end of file diff --git a/plugins/magpie-issue/skills/issue-reassess-stats b/plugins/magpie-issue/skills/issue-reassess-stats new file mode 120000 index 000000000..35fc4ffe0 --- /dev/null +++ b/plugins/magpie-issue/skills/issue-reassess-stats @@ -0,0 +1 @@ +../../../skills/issue-reassess-stats \ No newline at end of file diff --git a/plugins/magpie-issue/skills/issue-reproducer b/plugins/magpie-issue/skills/issue-reproducer new file mode 120000 index 000000000..1deca5bfd --- /dev/null +++ b/plugins/magpie-issue/skills/issue-reproducer @@ -0,0 +1 @@ +../../../skills/issue-reproducer \ No newline at end of file diff --git a/plugins/magpie-issue/skills/issue-stale-sweep b/plugins/magpie-issue/skills/issue-stale-sweep new file mode 120000 index 000000000..4bb581f40 --- /dev/null +++ b/plugins/magpie-issue/skills/issue-stale-sweep @@ -0,0 +1 @@ +../../../skills/issue-stale-sweep \ No newline at end of file diff --git a/plugins/magpie-issue/skills/issue-triage b/plugins/magpie-issue/skills/issue-triage new file mode 120000 index 000000000..e4c48d1ba --- /dev/null +++ b/plugins/magpie-issue/skills/issue-triage @@ -0,0 +1 @@ +../../../skills/issue-triage \ No newline at end of file diff --git a/plugins/magpie-mentoring/.claude-plugin/plugin.json b/plugins/magpie-mentoring/.claude-plugin/plugin.json new file mode 100644 index 000000000..c59f30eff --- /dev/null +++ b/plugins/magpie-mentoring/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "magpie-mentoring", + "description": "Apache Magpie \u2014 newcomer mentoring: welcome, newcomer-issue explanations, good-first-issue authoring + sweep.", + "skills": "./skills" +} diff --git a/plugins/magpie-mentoring/skills/good-first-issue-author b/plugins/magpie-mentoring/skills/good-first-issue-author new file mode 120000 index 000000000..0333f7f04 --- /dev/null +++ b/plugins/magpie-mentoring/skills/good-first-issue-author @@ -0,0 +1 @@ +../../../skills/good-first-issue-author \ No newline at end of file diff --git a/plugins/magpie-mentoring/skills/good-first-issue-sweep b/plugins/magpie-mentoring/skills/good-first-issue-sweep new file mode 120000 index 000000000..f5b5458ca --- /dev/null +++ b/plugins/magpie-mentoring/skills/good-first-issue-sweep @@ -0,0 +1 @@ +../../../skills/good-first-issue-sweep \ No newline at end of file diff --git a/plugins/magpie-mentoring/skills/mentoring-welcome b/plugins/magpie-mentoring/skills/mentoring-welcome new file mode 120000 index 000000000..27df0a2eb --- /dev/null +++ b/plugins/magpie-mentoring/skills/mentoring-welcome @@ -0,0 +1 @@ +../../../skills/mentoring-welcome \ No newline at end of file diff --git a/plugins/magpie-mentoring/skills/newcomer-issue-explainer b/plugins/magpie-mentoring/skills/newcomer-issue-explainer new file mode 120000 index 000000000..c6ecbde9e --- /dev/null +++ b/plugins/magpie-mentoring/skills/newcomer-issue-explainer @@ -0,0 +1 @@ +../../../skills/newcomer-issue-explainer \ No newline at end of file diff --git a/plugins/magpie-pairing/.claude-plugin/plugin.json b/plugins/magpie-pairing/.claude-plugin/plugin.json new file mode 100644 index 000000000..d6a868ea0 --- /dev/null +++ b/plugins/magpie-pairing/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "magpie-pairing", + "description": "Apache Magpie \u2014 pair a change with a structured self-review or a multi-agent adversarial review.", + "skills": "./skills" +} diff --git a/plugins/magpie-pairing/skills/pairing-multi-agent-review b/plugins/magpie-pairing/skills/pairing-multi-agent-review new file mode 120000 index 000000000..6219e2ba4 --- /dev/null +++ b/plugins/magpie-pairing/skills/pairing-multi-agent-review @@ -0,0 +1 @@ +../../../skills/pairing-multi-agent-review \ No newline at end of file diff --git a/plugins/magpie-pairing/skills/pairing-self-review b/plugins/magpie-pairing/skills/pairing-self-review new file mode 120000 index 000000000..84052f6e5 --- /dev/null +++ b/plugins/magpie-pairing/skills/pairing-self-review @@ -0,0 +1 @@ +../../../skills/pairing-self-review \ No newline at end of file diff --git a/plugins/magpie-pr-management/.claude-plugin/plugin.json b/plugins/magpie-pr-management/.claude-plugin/plugin.json new file mode 100644 index 000000000..573658805 --- /dev/null +++ b/plugins/magpie-pr-management/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "magpie-pr-management", + "description": "Apache Magpie \u2014 PR-queue management: triage, stats, deep code review, quick-merge, stale-sweep, reviewer routing, pre-first-PR checks.", + "skills": "./skills" +} diff --git a/plugins/magpie-pr-management/skills/pr-management-code-review b/plugins/magpie-pr-management/skills/pr-management-code-review new file mode 120000 index 000000000..831ee72b1 --- /dev/null +++ b/plugins/magpie-pr-management/skills/pr-management-code-review @@ -0,0 +1 @@ +../../../skills/pr-management-code-review \ No newline at end of file diff --git a/plugins/magpie-pr-management/skills/pr-management-mentor b/plugins/magpie-pr-management/skills/pr-management-mentor new file mode 120000 index 000000000..686e9d943 --- /dev/null +++ b/plugins/magpie-pr-management/skills/pr-management-mentor @@ -0,0 +1 @@ +../../../skills/pr-management-mentor \ No newline at end of file diff --git a/plugins/magpie-pr-management/skills/pr-management-quick-merge b/plugins/magpie-pr-management/skills/pr-management-quick-merge new file mode 120000 index 000000000..5f1ca0c25 --- /dev/null +++ b/plugins/magpie-pr-management/skills/pr-management-quick-merge @@ -0,0 +1 @@ +../../../skills/pr-management-quick-merge \ No newline at end of file diff --git a/plugins/magpie-pr-management/skills/pr-management-stats b/plugins/magpie-pr-management/skills/pr-management-stats new file mode 120000 index 000000000..e7513a192 --- /dev/null +++ b/plugins/magpie-pr-management/skills/pr-management-stats @@ -0,0 +1 @@ +../../../skills/pr-management-stats \ No newline at end of file diff --git a/plugins/magpie-pr-management/skills/pr-management-triage b/plugins/magpie-pr-management/skills/pr-management-triage new file mode 120000 index 000000000..e62d08d8f --- /dev/null +++ b/plugins/magpie-pr-management/skills/pr-management-triage @@ -0,0 +1 @@ +../../../skills/pr-management-triage \ No newline at end of file diff --git a/plugins/magpie-pr-management/skills/pr-stale-sweep b/plugins/magpie-pr-management/skills/pr-stale-sweep new file mode 120000 index 000000000..66f2e99ed --- /dev/null +++ b/plugins/magpie-pr-management/skills/pr-stale-sweep @@ -0,0 +1 @@ +../../../skills/pr-stale-sweep \ No newline at end of file diff --git a/plugins/magpie-pr-management/skills/pre-first-pr-check b/plugins/magpie-pr-management/skills/pre-first-pr-check new file mode 120000 index 000000000..f2bc033b2 --- /dev/null +++ b/plugins/magpie-pr-management/skills/pre-first-pr-check @@ -0,0 +1 @@ +../../../skills/pre-first-pr-check \ No newline at end of file diff --git a/plugins/magpie-pr-management/skills/reviewer-routing b/plugins/magpie-pr-management/skills/reviewer-routing new file mode 120000 index 000000000..989f9fc30 --- /dev/null +++ b/plugins/magpie-pr-management/skills/reviewer-routing @@ -0,0 +1 @@ +../../../skills/reviewer-routing \ No newline at end of file diff --git a/plugins/magpie-release-management/.claude-plugin/plugin.json b/plugins/magpie-release-management/.claude-plugin/plugin.json new file mode 100644 index 000000000..15bf156e1 --- /dev/null +++ b/plugins/magpie-release-management/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "magpie-release-management", + "description": "Apache Magpie \u2014 ASF release lifecycle: plan, RC cut/sign, vote, tally, promote, announce, archive, audit.", + "skills": "./skills" +} diff --git a/plugins/magpie-release-management/skills/release-announce-draft b/plugins/magpie-release-management/skills/release-announce-draft new file mode 120000 index 000000000..983a5efc6 --- /dev/null +++ b/plugins/magpie-release-management/skills/release-announce-draft @@ -0,0 +1 @@ +../../../skills/release-announce-draft \ No newline at end of file diff --git a/plugins/magpie-release-management/skills/release-archive-sweep b/plugins/magpie-release-management/skills/release-archive-sweep new file mode 120000 index 000000000..650df1824 --- /dev/null +++ b/plugins/magpie-release-management/skills/release-archive-sweep @@ -0,0 +1 @@ +../../../skills/release-archive-sweep \ No newline at end of file diff --git a/plugins/magpie-release-management/skills/release-audit-report b/plugins/magpie-release-management/skills/release-audit-report new file mode 120000 index 000000000..9b0d7aae8 --- /dev/null +++ b/plugins/magpie-release-management/skills/release-audit-report @@ -0,0 +1 @@ +../../../skills/release-audit-report \ No newline at end of file diff --git a/plugins/magpie-release-management/skills/release-keys-sync b/plugins/magpie-release-management/skills/release-keys-sync new file mode 120000 index 000000000..5c6f2bea2 --- /dev/null +++ b/plugins/magpie-release-management/skills/release-keys-sync @@ -0,0 +1 @@ +../../../skills/release-keys-sync \ No newline at end of file diff --git a/plugins/magpie-release-management/skills/release-prepare b/plugins/magpie-release-management/skills/release-prepare new file mode 120000 index 000000000..9fab6454a --- /dev/null +++ b/plugins/magpie-release-management/skills/release-prepare @@ -0,0 +1 @@ +../../../skills/release-prepare \ No newline at end of file diff --git a/plugins/magpie-release-management/skills/release-promote b/plugins/magpie-release-management/skills/release-promote new file mode 120000 index 000000000..38407b4f4 --- /dev/null +++ b/plugins/magpie-release-management/skills/release-promote @@ -0,0 +1 @@ +../../../skills/release-promote \ No newline at end of file diff --git a/plugins/magpie-release-management/skills/release-rc-cut b/plugins/magpie-release-management/skills/release-rc-cut new file mode 120000 index 000000000..6734629b7 --- /dev/null +++ b/plugins/magpie-release-management/skills/release-rc-cut @@ -0,0 +1 @@ +../../../skills/release-rc-cut \ No newline at end of file diff --git a/plugins/magpie-release-management/skills/release-verify-rc b/plugins/magpie-release-management/skills/release-verify-rc new file mode 120000 index 000000000..d4c32799b --- /dev/null +++ b/plugins/magpie-release-management/skills/release-verify-rc @@ -0,0 +1 @@ +../../../skills/release-verify-rc \ No newline at end of file diff --git a/plugins/magpie-release-management/skills/release-vote-draft b/plugins/magpie-release-management/skills/release-vote-draft new file mode 120000 index 000000000..92c924cfe --- /dev/null +++ b/plugins/magpie-release-management/skills/release-vote-draft @@ -0,0 +1 @@ +../../../skills/release-vote-draft \ No newline at end of file diff --git a/plugins/magpie-release-management/skills/release-vote-tally b/plugins/magpie-release-management/skills/release-vote-tally new file mode 120000 index 000000000..2c469d205 --- /dev/null +++ b/plugins/magpie-release-management/skills/release-vote-tally @@ -0,0 +1 @@ +../../../skills/release-vote-tally \ No newline at end of file diff --git a/plugins/magpie-repo-health/.claude-plugin/plugin.json b/plugins/magpie-repo-health/.claude-plugin/plugin.json new file mode 100644 index 000000000..02b1c1d7b --- /dev/null +++ b/plugins/magpie-repo-health/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "magpie-repo-health", + "description": "Apache Magpie \u2014 read-only repo-health audits: runner labels, workflow security, dependency/license/NOTICE, flaky tests, audit-finding fixes.", + "skills": "./skills" +} diff --git a/plugins/magpie-repo-health/skills/audit-finding-fix b/plugins/magpie-repo-health/skills/audit-finding-fix new file mode 120000 index 000000000..a606cc6ce --- /dev/null +++ b/plugins/magpie-repo-health/skills/audit-finding-fix @@ -0,0 +1 @@ +../../../skills/audit-finding-fix \ No newline at end of file diff --git a/plugins/magpie-repo-health/skills/ci-runner-audit b/plugins/magpie-repo-health/skills/ci-runner-audit new file mode 120000 index 000000000..e9c23b4d0 --- /dev/null +++ b/plugins/magpie-repo-health/skills/ci-runner-audit @@ -0,0 +1 @@ +../../../skills/ci-runner-audit \ No newline at end of file diff --git a/plugins/magpie-repo-health/skills/dependency-audit b/plugins/magpie-repo-health/skills/dependency-audit new file mode 120000 index 000000000..35b24b551 --- /dev/null +++ b/plugins/magpie-repo-health/skills/dependency-audit @@ -0,0 +1 @@ +../../../skills/dependency-audit \ No newline at end of file diff --git a/plugins/magpie-repo-health/skills/dependency-license-audit b/plugins/magpie-repo-health/skills/dependency-license-audit new file mode 120000 index 000000000..1fac0ccd7 --- /dev/null +++ b/plugins/magpie-repo-health/skills/dependency-license-audit @@ -0,0 +1 @@ +../../../skills/dependency-license-audit \ No newline at end of file diff --git a/plugins/magpie-repo-health/skills/flaky-test-triage b/plugins/magpie-repo-health/skills/flaky-test-triage new file mode 120000 index 000000000..993b2ad9f --- /dev/null +++ b/plugins/magpie-repo-health/skills/flaky-test-triage @@ -0,0 +1 @@ +../../../skills/flaky-test-triage \ No newline at end of file diff --git a/plugins/magpie-repo-health/skills/license-compliance-audit b/plugins/magpie-repo-health/skills/license-compliance-audit new file mode 120000 index 000000000..96861730d --- /dev/null +++ b/plugins/magpie-repo-health/skills/license-compliance-audit @@ -0,0 +1 @@ +../../../skills/license-compliance-audit \ No newline at end of file diff --git a/plugins/magpie-repo-health/skills/workflow-security-audit b/plugins/magpie-repo-health/skills/workflow-security-audit new file mode 120000 index 000000000..eedd5ab9f --- /dev/null +++ b/plugins/magpie-repo-health/skills/workflow-security-audit @@ -0,0 +1 @@ +../../../skills/workflow-security-audit \ No newline at end of file diff --git a/plugins/magpie-security/.claude-plugin/plugin.json b/plugins/magpie-security/.claude-plugin/plugin.json new file mode 100644 index 000000000..41ad7c4bf --- /dev/null +++ b/plugins/magpie-security/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "magpie-security", + "description": "Apache Magpie \u2014 security-issue handling lifecycle \u2014 import through CVE publication, triage, sync, dedup, invalidate. Maintainer-only.", + "skills": "./skills" +} diff --git a/plugins/magpie-security/skills/security-cve-allocate b/plugins/magpie-security/skills/security-cve-allocate new file mode 120000 index 000000000..4b8ddcc84 --- /dev/null +++ b/plugins/magpie-security/skills/security-cve-allocate @@ -0,0 +1 @@ +../../../skills/security-cve-allocate \ No newline at end of file diff --git a/plugins/magpie-security/skills/security-issue-deduplicate b/plugins/magpie-security/skills/security-issue-deduplicate new file mode 120000 index 000000000..27467f599 --- /dev/null +++ b/plugins/magpie-security/skills/security-issue-deduplicate @@ -0,0 +1 @@ +../../../skills/security-issue-deduplicate \ No newline at end of file diff --git a/plugins/magpie-security/skills/security-issue-fix b/plugins/magpie-security/skills/security-issue-fix new file mode 120000 index 000000000..ca3a85797 --- /dev/null +++ b/plugins/magpie-security/skills/security-issue-fix @@ -0,0 +1 @@ +../../../skills/security-issue-fix \ No newline at end of file diff --git a/plugins/magpie-security/skills/security-issue-import b/plugins/magpie-security/skills/security-issue-import new file mode 120000 index 000000000..f2e97f241 --- /dev/null +++ b/plugins/magpie-security/skills/security-issue-import @@ -0,0 +1 @@ +../../../skills/security-issue-import \ No newline at end of file diff --git a/plugins/magpie-security/skills/security-issue-import-from-md b/plugins/magpie-security/skills/security-issue-import-from-md new file mode 120000 index 000000000..44c90e3c1 --- /dev/null +++ b/plugins/magpie-security/skills/security-issue-import-from-md @@ -0,0 +1 @@ +../../../skills/security-issue-import-from-md \ No newline at end of file diff --git a/plugins/magpie-security/skills/security-issue-import-from-pr b/plugins/magpie-security/skills/security-issue-import-from-pr new file mode 120000 index 000000000..b640559cb --- /dev/null +++ b/plugins/magpie-security/skills/security-issue-import-from-pr @@ -0,0 +1 @@ +../../../skills/security-issue-import-from-pr \ No newline at end of file diff --git a/plugins/magpie-security/skills/security-issue-import-from-scan b/plugins/magpie-security/skills/security-issue-import-from-scan new file mode 120000 index 000000000..75d352a6b --- /dev/null +++ b/plugins/magpie-security/skills/security-issue-import-from-scan @@ -0,0 +1 @@ +../../../skills/security-issue-import-from-scan \ No newline at end of file diff --git a/plugins/magpie-security/skills/security-issue-import-via-forwarder b/plugins/magpie-security/skills/security-issue-import-via-forwarder new file mode 120000 index 000000000..4b835055d --- /dev/null +++ b/plugins/magpie-security/skills/security-issue-import-via-forwarder @@ -0,0 +1 @@ +../../../skills/security-issue-import-via-forwarder \ No newline at end of file diff --git a/plugins/magpie-security/skills/security-issue-invalidate b/plugins/magpie-security/skills/security-issue-invalidate new file mode 120000 index 000000000..c35602c5b --- /dev/null +++ b/plugins/magpie-security/skills/security-issue-invalidate @@ -0,0 +1 @@ +../../../skills/security-issue-invalidate \ No newline at end of file diff --git a/plugins/magpie-security/skills/security-issue-sync b/plugins/magpie-security/skills/security-issue-sync new file mode 120000 index 000000000..0f524f33f --- /dev/null +++ b/plugins/magpie-security/skills/security-issue-sync @@ -0,0 +1 @@ +../../../skills/security-issue-sync \ No newline at end of file diff --git a/plugins/magpie-security/skills/security-issue-triage b/plugins/magpie-security/skills/security-issue-triage new file mode 120000 index 000000000..bbe501c5e --- /dev/null +++ b/plugins/magpie-security/skills/security-issue-triage @@ -0,0 +1 @@ +../../../skills/security-issue-triage \ No newline at end of file diff --git a/plugins/magpie-security/skills/security-tracker-stats-dashboard b/plugins/magpie-security/skills/security-tracker-stats-dashboard new file mode 120000 index 000000000..912859aa6 --- /dev/null +++ b/plugins/magpie-security/skills/security-tracker-stats-dashboard @@ -0,0 +1 @@ +../../../skills/security-tracker-stats-dashboard \ No newline at end of file diff --git a/plugins/magpie-setup/.claude-plugin/plugin.json b/plugins/magpie-setup/.claude-plugin/plugin.json new file mode 100644 index 000000000..8c55abd77 --- /dev/null +++ b/plugins/magpie-setup/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "magpie-setup", + "description": "Apache Magpie \u2014 framework install/maintenance: install (adopt), upgrade, verify, override, status, secure-agent setup, shared-config sync.", + "skills": "./skills" +} diff --git a/plugins/magpie-setup/skills/setup b/plugins/magpie-setup/skills/setup new file mode 120000 index 000000000..8ae30b1a9 --- /dev/null +++ b/plugins/magpie-setup/skills/setup @@ -0,0 +1 @@ +../../../skills/setup \ No newline at end of file diff --git a/plugins/magpie-setup/skills/setup-isolated-setup-doctor b/plugins/magpie-setup/skills/setup-isolated-setup-doctor new file mode 120000 index 000000000..729daa07a --- /dev/null +++ b/plugins/magpie-setup/skills/setup-isolated-setup-doctor @@ -0,0 +1 @@ +../../../skills/setup-isolated-setup-doctor \ No newline at end of file diff --git a/plugins/magpie-setup/skills/setup-isolated-setup-install b/plugins/magpie-setup/skills/setup-isolated-setup-install new file mode 120000 index 000000000..b142191ff --- /dev/null +++ b/plugins/magpie-setup/skills/setup-isolated-setup-install @@ -0,0 +1 @@ +../../../skills/setup-isolated-setup-install \ No newline at end of file diff --git a/plugins/magpie-setup/skills/setup-isolated-setup-update b/plugins/magpie-setup/skills/setup-isolated-setup-update new file mode 120000 index 000000000..e1dd883e5 --- /dev/null +++ b/plugins/magpie-setup/skills/setup-isolated-setup-update @@ -0,0 +1 @@ +../../../skills/setup-isolated-setup-update \ No newline at end of file diff --git a/plugins/magpie-setup/skills/setup-isolated-setup-verify b/plugins/magpie-setup/skills/setup-isolated-setup-verify new file mode 120000 index 000000000..31a127698 --- /dev/null +++ b/plugins/magpie-setup/skills/setup-isolated-setup-verify @@ -0,0 +1 @@ +../../../skills/setup-isolated-setup-verify \ No newline at end of file diff --git a/plugins/magpie-setup/skills/setup-override-upstream b/plugins/magpie-setup/skills/setup-override-upstream new file mode 120000 index 000000000..fb63dc407 --- /dev/null +++ b/plugins/magpie-setup/skills/setup-override-upstream @@ -0,0 +1 @@ +../../../skills/setup-override-upstream \ No newline at end of file diff --git a/plugins/magpie-setup/skills/setup-shared-config-sync b/plugins/magpie-setup/skills/setup-shared-config-sync new file mode 120000 index 000000000..236211e34 --- /dev/null +++ b/plugins/magpie-setup/skills/setup-shared-config-sync @@ -0,0 +1 @@ +../../../skills/setup-shared-config-sync \ No newline at end of file diff --git a/plugins/magpie-setup/skills/setup-status b/plugins/magpie-setup/skills/setup-status new file mode 120000 index 000000000..ab87fce5a --- /dev/null +++ b/plugins/magpie-setup/skills/setup-status @@ -0,0 +1 @@ +../../../skills/setup-status \ No newline at end of file diff --git a/plugins/magpie-setup/skills/setup-upstream-fix b/plugins/magpie-setup/skills/setup-upstream-fix new file mode 120000 index 000000000..3fa8f25df --- /dev/null +++ b/plugins/magpie-setup/skills/setup-upstream-fix @@ -0,0 +1 @@ +../../../skills/setup-upstream-fix \ No newline at end of file diff --git a/plugins/magpie-utilities/.claude-plugin/plugin.json b/plugins/magpie-utilities/.claude-plugin/plugin.json new file mode 100644 index 000000000..c8fa0933b --- /dev/null +++ b/plugins/magpie-utilities/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "magpie-utilities", + "description": "Apache Magpie \u2014 framework meta-skills: write-skill, optimize-skill, skill-reconciler, list-skills.", + "skills": "./skills" +} diff --git a/plugins/magpie-utilities/skills/list-skills b/plugins/magpie-utilities/skills/list-skills new file mode 120000 index 000000000..97f92b85a --- /dev/null +++ b/plugins/magpie-utilities/skills/list-skills @@ -0,0 +1 @@ +../../../skills/list-skills \ No newline at end of file diff --git a/plugins/magpie-utilities/skills/optimize-skill b/plugins/magpie-utilities/skills/optimize-skill new file mode 120000 index 000000000..f25d4c312 --- /dev/null +++ b/plugins/magpie-utilities/skills/optimize-skill @@ -0,0 +1 @@ +../../../skills/optimize-skill \ No newline at end of file diff --git a/plugins/magpie-utilities/skills/skill-reconciler b/plugins/magpie-utilities/skills/skill-reconciler new file mode 120000 index 000000000..495f0f27e --- /dev/null +++ b/plugins/magpie-utilities/skills/skill-reconciler @@ -0,0 +1 @@ +../../../skills/skill-reconciler \ No newline at end of file diff --git a/plugins/magpie-utilities/skills/write-skill b/plugins/magpie-utilities/skills/write-skill new file mode 120000 index 000000000..2d8e4f960 --- /dev/null +++ b/plugins/magpie-utilities/skills/write-skill @@ -0,0 +1 @@ +../../../skills/write-skill \ No newline at end of file From 5c767152dc5c681ffca1315503d6ed779a7bd0da Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 17:07:32 +0200 Subject: [PATCH 09/14] build(prek): add check-family-plugins hook; document Windows symlink caveat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tools/dev/check-family-plugins.py: validates that plugins/magpie-/ match the skills' family: frontmatter (each family's skills present as single-hop symlinks into skills/, and listed in marketplace.json). Catches a new skill, a changed family, or a stale symlink. --fix regenerates them. - Wire it as a local prek hook (runs when a SKILL.md, a family plugin, or the marketplace manifest changes). - docs/setup/marketplaces.md: document that per-family plugins rely on git symlinks — on Windows enable core.symlinks + Developer Mode, or use the all-in-one magpie plugin (no symlinks). Verified symlinks survive a GitHub clone on macOS. --- .pre-commit-config.yaml | 14 +++ docs/setup/marketplaces.md | 13 +++ tools/dev/check-family-plugins.py | 160 ++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 tools/dev/check-family-plugins.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 854294ad5..61978011e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -233,6 +233,20 @@ repos: files: ^(\.gitattributes|\.(agents|claude|github|kiro)/skills/.*|skills/.*|projects/_template/.*)$ types_or: [symlink, file] pass_filenames: false + # Family-plugin consistency guard. The per-family marketplace plugins under + # plugins/magpie-/ must stay in sync with the skills' `family:` + # frontmatter (the source of truth): each family's skills present as + # single-hop symlinks into the shared skills/ tree, and each family listed in + # .claude-plugin/marketplace.json alongside the all-in-one `magpie`. Catches a + # new skill, a changed family, or a stale symlink. `--fix` regenerates them. + - repo: local + hooks: + - id: check-family-plugins + name: check-family-plugins (marketplace families vs frontmatter) + language: system + entry: python3 tools/dev/check-family-plugins.py + files: ^(skills/.*/SKILL\.md|plugins/.*|\.claude-plugin/marketplace\.json)$ + pass_filenames: false # Workspace-membership guard. The single source of truth for # which Python projects get pre-commit hooks + the CI pytest # matrix is the `[tool.uv.workspace] members` array in the root diff --git a/docs/setup/marketplaces.md b/docs/setup/marketplaces.md index 085bb1aab..4fee76983 100644 --- a/docs/setup/marketplaces.md +++ b/docs/setup/marketplaces.md @@ -82,6 +82,19 @@ Skills are invoked under the installing plugin's namespace — e.g. Per-family plugins reference the shared `skills/` tree via symlinks (no copies), so there is a single source of truth for every skill. +> [!IMPORTANT] +> **Windows + per-family plugins.** The per-family plugins rely on git symlinks +> (each `plugins/magpie-/skills/` links to the shared +> `skills/`). Git for Windows does **not** materialise real symlinks +> unless `core.symlinks` is enabled *and* the account may create them (Windows +> Developer Mode, or an elevated shell) — otherwise the clone writes each +> symlink as a plain text file and that family's skills won't load. On Windows, +> either enable symlink support +> (`git config --global core.symlinks true` + Developer Mode) **or** install the +> **all-in-one `magpie` plugin**, which uses the real `skills/` directory and +> needs no symlinks. macOS and Linux are unaffected. (Verified on macOS: a +> `/plugin marketplace add` GitHub clone preserves and resolves the symlinks.) + ## Supported agents Every method below uses the **GitHub repository diff --git a/tools/dev/check-family-plugins.py b/tools/dev/check-family-plugins.py new file mode 100644 index 000000000..ee6a36ad5 --- /dev/null +++ b/tools/dev/check-family-plugins.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +# https://www.apache.org/licenses/LICENSE-2.0 +"""Validate the per-family marketplace plugins against the skills' `family:` +frontmatter — the source of truth. + +For every family that skills declare in their `family:` frontmatter there must +be a `plugins/magpie-/` plugin whose `skills/` directory contains +exactly that family's skills as single-hop symlinks into the shared +`skills/` tree, and `.claude-plugin/marketplace.json` must list it (plus +the all-in-one `magpie`). Drift — a new skill, a changed family, a stale +symlink — fails the check. + +Run with `--fix` to regenerate the family plugins + symlinks + marketplace +entries from the frontmatter. +""" +from __future__ import annotations + +import argparse +import glob +import json +import os +import re +import sys + +MARKETPLACE = ".claude-plugin/marketplace.json" +SYMLINK_TARGET = "../../../skills/{skill}" # relative to plugins/magpie-/skills/ + +# Per-family descriptions used when (re)generating manifests. Keep in sync with +# the family README; `check` does not enforce these (only structure/symlinks). +DESC = { + "security": "security-issue handling lifecycle — import through CVE publication, triage, sync, dedup, invalidate. Maintainer-only.", + "release-management": "ASF release lifecycle: plan, RC cut/sign, vote, tally, promote, announce, archive, audit.", + "setup": "framework install/maintenance: install (adopt), upgrade, verify, override, status, secure-agent setup, shared-config sync.", + "pr-management": "PR-queue management: triage, stats, deep code review, quick-merge, stale-sweep, reviewer routing, pre-first-PR checks.", + "issue": "issue lifecycle: triage, reproduction, fix drafting, reassess, stale-sweep, dedup, backlog stats.", + "repo-health": "read-only repo-health audits: runner labels, workflow security, dependency/license/NOTICE, flaky tests, audit-finding fixes.", + "contributor-growth": "path-to-committer: activity sweeps, nominations, sentiment, readiness, committer/post-vote onboarding.", + "utilities": "framework meta-skills: write-skill, optimize-skill, skill-reconciler, list-skills.", + "mentoring": "newcomer mentoring: welcome, newcomer-issue explanations, good-first-issue authoring + sweep.", + "pairing": "pair a change with a structured self-review or a multi-agent adversarial review.", +} + + +def families_from_frontmatter() -> dict[str, set[str]]: + fam: dict[str, set[str]] = {} + for path in sorted(glob.glob("skills/*/SKILL.md")): + m = re.search(r"^family:\s*(\S+)", open(path, encoding="utf-8").read(), re.M) + if m: + fam.setdefault(m.group(1), set()).add(os.path.basename(os.path.dirname(path))) + return fam + + +def check(fam: dict[str, set[str]]) -> list[str]: + errors: list[str] = [] + + try: + market = json.load(open(MARKETPLACE, encoding="utf-8")) + listed = {p["name"] for p in market.get("plugins", [])} + except (OSError, ValueError) as exc: + return [f"{MARKETPLACE}: cannot read/parse ({exc})"] + + if "magpie" not in listed: + errors.append(f"{MARKETPLACE}: missing the all-in-one 'magpie' plugin entry") + + for family, skills in sorted(fam.items()): + name = f"magpie-{family}" + pdir = f"plugins/{name}" + if not os.path.isfile(f"{pdir}/.claude-plugin/plugin.json"): + errors.append(f"{name}: missing {pdir}/.claude-plugin/plugin.json") + continue + if name not in listed: + errors.append(f"{MARKETPLACE}: missing entry for '{name}'") + + sdir = f"{pdir}/skills" + have: dict[str, str] = {} + for entry in os.listdir(sdir) if os.path.isdir(sdir) else []: + fp = os.path.join(sdir, entry) + if os.path.islink(fp): + have[entry] = os.readlink(fp) + else: + errors.append(f"{name}: {fp} is not a symlink") + + for skill in sorted(skills - set(have)): + errors.append(f"{name}: missing symlink for '{skill}' (family={family})") + for skill in sorted(set(have) - skills): + errors.append(f"{name}: stale symlink '{skill}' — its skill is not family={family}") + for skill in sorted(skills & set(have)): + want = SYMLINK_TARGET.format(skill=skill) + if have[skill] != want: + errors.append(f"{name}: {sdir}/{skill} -> {have[skill]} (expected {want})") + + for pdir in sorted(glob.glob("plugins/magpie-*")): + family = os.path.basename(pdir)[len("magpie-"):] + if family not in fam: + errors.append(f"orphan plugin '{os.path.basename(pdir)}': no skill declares family '{family}'") + + return errors + + +def fix(fam: dict[str, set[str]]) -> None: + import shutil + + for pdir in glob.glob("plugins/magpie-*"): + shutil.rmtree(pdir) + for family, skills in sorted(fam.items()): + name = f"magpie-{family}" + sdir = f"plugins/{name}/skills" + os.makedirs(f"plugins/{name}/.claude-plugin") + os.makedirs(sdir) + for skill in sorted(skills): + os.symlink(SYMLINK_TARGET.format(skill=skill), os.path.join(sdir, skill)) + manifest = { + "name": name, + "description": f"Apache Magpie — {DESC.get(family, family + ' family skills')}", + "skills": "./skills", + } + with open(f"plugins/{name}/.claude-plugin/plugin.json", "w", encoding="utf-8") as fh: + json.dump(manifest, fh, indent=2) + fh.write("\n") + + market = json.load(open(MARKETPLACE, encoding="utf-8")) + keep = [p for p in market["plugins"] if p["name"] == "magpie"] + for family, skills in sorted(fam.items()): + keep.append({ + "name": f"magpie-{family}", + "source": f"./plugins/magpie-{family}", + "version": "0.2.0", + "description": f"Apache Magpie {family} family ({len(skills)} skills).", + }) + market["plugins"] = keep + with open(MARKETPLACE, "w", encoding="utf-8") as fh: + json.dump(market, fh, indent=2) + fh.write("\n") + print("Regenerated per-family plugins + marketplace entries from frontmatter.") + + +def main() -> int: + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("--fix", action="store_true", help="regenerate plugins from frontmatter") + ap.add_argument("files", nargs="*", help="(ignored; present for pre-commit)") + args = ap.parse_args() + + fam = families_from_frontmatter() + if args.fix: + fix(fam) + return 0 + + errors = check(fam) + if errors: + print("Family plugins are out of sync with skills' `family:` frontmatter:", file=sys.stderr) + for e in errors: + print(f" - {e}", file=sys.stderr) + print("\nRun `python3 tools/dev/check-family-plugins.py --fix` to regenerate.", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 8ea6df9e57a7c8779039045881f004915b4d6f8d Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 17:08:50 +0200 Subject: [PATCH 10/14] docs(marketplaces): explain all-in-one vs per-family install trade-offs Spell out that you can install either the all-in-one magpie plugin or any number of per-family plugins (and mix families), with the pros/cons of each: simplicity + Windows-safe (no symlinks) vs proportional always-on token cost. --- docs/setup/marketplaces.md | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/docs/setup/marketplaces.md b/docs/setup/marketplaces.md index 4fee76983..265caf923 100644 --- a/docs/setup/marketplaces.md +++ b/docs/setup/marketplaces.md @@ -51,15 +51,35 @@ through the plugin/extension mechanisms of the major AI coding agents, ## Choosing a plugin: all-in-one vs per-family -The framework ships as **eleven** plugins so you install only what you use: - -- **`magpie`** (all-in-one) — every skill, all ten families. Simplest, but its - always-on metadata costs **~21.7k tokens in every session** (each installed - skill advertises a short description to the model on every turn — see - ["always-on" cost](#versioning) below). -- **Per-family plugins** — `magpie-security`, `magpie-release-management`, … — - install only the families you use, so only their skills are always-on. - **Recommended.** +The framework ships as **eleven** plugins. You can install **either** the +all-in-one plugin **or** any number of per-family plugins — and you can mix +several families. Pick based on the trade-off between install simplicity and +always-on token cost (each installed skill advertises a short description to +the model on **every** turn — see ["always-on" cost](#versioning) below). + +**All-in-one — `magpie`** + +- ✅ One install; all 70 skills; nothing to decide. Uses the real `skills/` + directory, so **no symlinks** — works on Windows out of the box. +- ⚠️ Adds **~21.7k always-on tokens to every session**, including families you + may never use — that context (and cost) is spent whether or not you invoke a + Magpie skill that turn. +- Best when you genuinely want everything, or you're on Windows without symlink + support. + +**Per-family — `magpie-`** *(recommended)* + +- ✅ Install only the families you use, so the always-on cost is proportional + (`magpie-security` ≈ 3.9k, `magpie-pairing` ≈ 0.6k). Install several to mix + and match. +- ⚠️ You manage a few installs instead of one; adding a family later is a + separate install; relies on git symlinks (see the Windows note below). +- Best for day-to-day use where you want a lean context window. + +Mixing is fine — e.g. install `magpie-release-management` + `magpie-security` +and nothing else. The two are **not** exclusive with the all-in-one either, but +installing both `magpie` *and* a family plugin just double-loads those skills, +so pick one approach. | Family plugin | Skills | ~Always-on tokens | |---|---|---| From a8ca2fdf9c2262f3290492e92c9b9766f6b1874c Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 17:12:09 +0200 Subject: [PATCH 11/14] docs(marketplaces): flag marketplace support as experimental; point to the portable non-marketplace install MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a WARNING callout: plugin/marketplace mechanisms across the agentic CLIs (Claude Code, Codex, Copilot, Gemini, apm) are experimental and change between releases. The non-marketplace /magpie-setup install from the signed SVN release or the GitHub repo is always available, harness-neutral, and portable — it works on every agentic CLI via the universal .agents/skills/ layout, not just the ones with a marketplace. --- docs/setup/marketplaces.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/setup/marketplaces.md b/docs/setup/marketplaces.md index 265caf923..40d9c9213 100644 --- a/docs/setup/marketplaces.md +++ b/docs/setup/marketplaces.md @@ -49,6 +49,21 @@ through the plugin/extension mechanisms of the major AI coding agents, > `X.Y.Z` git tag; they are derived from — not a substitute for — the ASF > source release. +> [!WARNING] +> **Marketplace/plugin support is experimental in most agentic CLIs.** The +> plugin and marketplace mechanisms of Claude Code, Codex, Copilot, Gemini, +> `apm`, and others are new and evolving — manifest schemas and install +> commands change between releases (see [Verification status](#verification-status)). +> If a marketplace install breaks, or your agent has no marketplace at all, the +> **non-marketplace install is always available, universal, and portable**: +> adopt Magpie with `/magpie-setup` from either the **signed SVN release** +> (`dist.apache.org`) or the **GitHub repo** (git tag or branch) — see +> [`install-recipes.md`](install-recipes.md). That path is **harness-neutral**: +> it wires the skills into *any* agent's directory via the universal +> `.agents/skills/` layout, so it works on **every** agentic CLI — not only the +> ones with a marketplace. Rule of thumb: use a marketplace for a quick trial +> on a supported agent; use `/magpie-setup` for a stable, portable install. + ## Choosing a plugin: all-in-one vs per-family The framework ships as **eleven** plugins. You can install **either** the From f71c94b6bfadf32b02f7c2bb3b0bc82df03d9210 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 17:16:37 +0200 Subject: [PATCH 12/14] docs(marketplaces): explain skill-name differences across install methods Same skill, different invocation name: portable /magpie-setup install bakes a magpie- prefix into each skill (invoked /magpie-), whereas a marketplace plugin namespaces with plugin:skill and keeps the bare name (/magpie: for the all-in-one, /magpie-: for a family plugin). Adds a comparison table + examples and notes that repo cross-references use the portable form. --- docs/setup/marketplaces.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/docs/setup/marketplaces.md b/docs/setup/marketplaces.md index 40d9c9213..daf1e2f6e 100644 --- a/docs/setup/marketplaces.md +++ b/docs/setup/marketplaces.md @@ -7,6 +7,7 @@ - [Installing Apache Magpie from agent marketplaces](#installing-apache-magpie-from-agent-marketplaces) - [Choosing a plugin: all-in-one vs per-family](#choosing-a-plugin-all-in-one-vs-per-family) + - [Skill names differ by install method](#skill-names-differ-by-install-method) - [Supported agents](#supported-agents) - [Claude Code](#claude-code) - [OpenAI Codex CLI](#openai-codex-cli) @@ -130,6 +131,39 @@ so there is a single source of truth for every skill. > needs no symlinks. macOS and Linux are unaffected. (Verified on macOS: a > `/plugin marketplace add` GitHub clone preserves and resolves the symlinks.) +## Skill names differ by install method + +The **same skill** is invoked by a **different name** depending on how you +installed it. The portable `/magpie-setup` install bakes a `magpie-` prefix into +each skill's name (so framework skills never collide with your own); the +marketplace plugins namespace with `plugin:skill` and keep the bare skill name. + +| Skill (directory) | Portable — `/magpie-setup` snapshot | Marketplace — all-in-one `magpie` | Marketplace — family plugin | +|---|---|---|---| +| `release-vote-tally` | `/magpie-release-vote-tally` | `/magpie:release-vote-tally` | `/magpie-release-management:release-vote-tally` | +| `security-issue-triage` | `/magpie-security-issue-triage` | `/magpie:security-issue-triage` | `/magpie-security:security-issue-triage` | +| `setup` | `/magpie-setup` | `/magpie:setup` | `/magpie-setup:setup` | + +Why the difference: + +- **Portable install** (`/magpie-setup` snapshot) — the `setup` skill symlinks + each framework skill under a `magpie-` entry (e.g. + `skills/release-vote-tally/` → `magpie-release-vote-tally`), and the skill's + own frontmatter `name:` carries the same `magpie-` prefix. It is therefore + invoked as a **single hyphenated token**, `/magpie-`. The prefix *is* the + namespace — it keeps framework skills from clashing with the adopter's own + skills. +- **Marketplace install** — the **plugin name** is the namespace, applied with a + **colon**: `/:`. The `magpie-` frontmatter prefix is ignored + (the plugin already namespaces), so the skill keeps its bare directory name. + With the all-in-one plugin that's `/magpie:`; with a family plugin it's + `/magpie-:`. + +Throughout this repo's own docs and skills, cross-references use the +**portable** form (`/magpie-`), because that is the canonical install. +When you install via a marketplace, translate `/magpie-` to +`/:` (drop the `magpie-` prefix, add the plugin namespace). + ## Supported agents Every method below uses the **GitHub repository From 5be6e34760fae97c1c974a6f11e4ab8818215832 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 17:28:21 +0200 Subject: [PATCH 13/14] fix(security): close files in check-family-plugins; use pathlib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves the 3 CodeQL 'file is not always closed' alerts (35/36/37) flagged on #907. Rewrite the family-plugin validator with pathlib — Path.read_text / write_text open-and-close by construction, so no dangling handles — and the same read/write/symlink logic reads cleaner. Behaviour unchanged (check/--fix verified: passes, idempotent, catches drift). --- tools/dev/check-family-plugins.py | 68 +++++++++++++++---------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/tools/dev/check-family-plugins.py b/tools/dev/check-family-plugins.py index ee6a36ad5..f0096ac4d 100644 --- a/tools/dev/check-family-plugins.py +++ b/tools/dev/check-family-plugins.py @@ -17,13 +17,15 @@ from __future__ import annotations import argparse -import glob import json -import os import re +import shutil import sys +from pathlib import Path -MARKETPLACE = ".claude-plugin/marketplace.json" +SKILLS = Path("skills") +PLUGINS = Path("plugins") +MARKETPLACE = Path(".claude-plugin/marketplace.json") SYMLINK_TARGET = "../../../skills/{skill}" # relative to plugins/magpie-/skills/ # Per-family descriptions used when (re)generating manifests. Keep in sync with @@ -44,10 +46,10 @@ def families_from_frontmatter() -> dict[str, set[str]]: fam: dict[str, set[str]] = {} - for path in sorted(glob.glob("skills/*/SKILL.md")): - m = re.search(r"^family:\s*(\S+)", open(path, encoding="utf-8").read(), re.M) + for path in sorted(SKILLS.glob("*/SKILL.md")): + m = re.search(r"^family:\s*(\S+)", path.read_text(encoding="utf-8"), re.M) if m: - fam.setdefault(m.group(1), set()).add(os.path.basename(os.path.dirname(path))) + fam.setdefault(m.group(1), set()).add(path.parent.name) return fam @@ -55,7 +57,7 @@ def check(fam: dict[str, set[str]]) -> list[str]: errors: list[str] = [] try: - market = json.load(open(MARKETPLACE, encoding="utf-8")) + market = json.loads(MARKETPLACE.read_text(encoding="utf-8")) listed = {p["name"] for p in market.get("plugins", [])} except (OSError, ValueError) as exc: return [f"{MARKETPLACE}: cannot read/parse ({exc})"] @@ -65,61 +67,59 @@ def check(fam: dict[str, set[str]]) -> list[str]: for family, skills in sorted(fam.items()): name = f"magpie-{family}" - pdir = f"plugins/{name}" - if not os.path.isfile(f"{pdir}/.claude-plugin/plugin.json"): + pdir = PLUGINS / name + if not (pdir / ".claude-plugin" / "plugin.json").is_file(): errors.append(f"{name}: missing {pdir}/.claude-plugin/plugin.json") continue if name not in listed: errors.append(f"{MARKETPLACE}: missing entry for '{name}'") - sdir = f"{pdir}/skills" - have: dict[str, str] = {} - for entry in os.listdir(sdir) if os.path.isdir(sdir) else []: - fp = os.path.join(sdir, entry) - if os.path.islink(fp): - have[entry] = os.readlink(fp) + sdir = pdir / "skills" + have: dict[str, Path] = {} + for entry in sorted(sdir.iterdir()) if sdir.is_dir() else []: + if entry.is_symlink(): + have[entry.name] = entry.readlink() else: - errors.append(f"{name}: {fp} is not a symlink") + errors.append(f"{name}: {entry} is not a symlink") for skill in sorted(skills - set(have)): errors.append(f"{name}: missing symlink for '{skill}' (family={family})") for skill in sorted(set(have) - skills): errors.append(f"{name}: stale symlink '{skill}' — its skill is not family={family}") for skill in sorted(skills & set(have)): - want = SYMLINK_TARGET.format(skill=skill) + want = Path(SYMLINK_TARGET.format(skill=skill)) if have[skill] != want: - errors.append(f"{name}: {sdir}/{skill} -> {have[skill]} (expected {want})") + errors.append(f"{name}: {sdir / skill} -> {have[skill]} (expected {want})") - for pdir in sorted(glob.glob("plugins/magpie-*")): - family = os.path.basename(pdir)[len("magpie-"):] + for pdir in sorted(PLUGINS.glob("magpie-*")): + family = pdir.name[len("magpie-"):] if family not in fam: - errors.append(f"orphan plugin '{os.path.basename(pdir)}': no skill declares family '{family}'") + errors.append(f"orphan plugin '{pdir.name}': no skill declares family '{family}'") return errors def fix(fam: dict[str, set[str]]) -> None: - import shutil - - for pdir in glob.glob("plugins/magpie-*"): + for pdir in PLUGINS.glob("magpie-*"): shutil.rmtree(pdir) for family, skills in sorted(fam.items()): name = f"magpie-{family}" - sdir = f"plugins/{name}/skills" - os.makedirs(f"plugins/{name}/.claude-plugin") - os.makedirs(sdir) + pdir = PLUGINS / name + sdir = pdir / "skills" + (pdir / ".claude-plugin").mkdir(parents=True) + sdir.mkdir(parents=True) for skill in sorted(skills): - os.symlink(SYMLINK_TARGET.format(skill=skill), os.path.join(sdir, skill)) + (sdir / skill).symlink_to(SYMLINK_TARGET.format(skill=skill)) manifest = { "name": name, "description": f"Apache Magpie — {DESC.get(family, family + ' family skills')}", "skills": "./skills", } - with open(f"plugins/{name}/.claude-plugin/plugin.json", "w", encoding="utf-8") as fh: - json.dump(manifest, fh, indent=2) - fh.write("\n") + (pdir / ".claude-plugin" / "plugin.json").write_text( + json.dumps(manifest, indent=2) + "\n", encoding="utf-8" + ) - market = json.load(open(MARKETPLACE, encoding="utf-8")) + market = json.loads(MARKETPLACE.read_text(encoding="utf-8")) keep = [p for p in market["plugins"] if p["name"] == "magpie"] for family, skills in sorted(fam.items()): keep.append({ @@ -129,9 +129,7 @@ def fix(fam: dict[str, set[str]]) -> None: "description": f"Apache Magpie {family} family ({len(skills)} skills).", }) market["plugins"] = keep - with open(MARKETPLACE, "w", encoding="utf-8") as fh: - json.dump(market, fh, indent=2) - fh.write("\n") + MARKETPLACE.write_text(json.dumps(market, indent=2) + "\n", encoding="utf-8") print("Regenerated per-family plugins + marketplace entries from frontmatter.") From 51bbfb261f477ffc8d5af1bfa1adfee7b776495d Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 19 Jul 2026 17:44:08 +0200 Subject: [PATCH 14/14] build(prek): validate all plugin manifests, not just family symlinks Extend check-family-plugins to confirm every plugin is properly defined: - all-in-one 'magpie' manifest names itself, declares skills: ./skills, and wires the hooks/check-upgrade.sh SessionStart hook (which must exist); - every marketplace.json entry resolves to a matching, uniquely-named plugin.json (catches dangling sources, name mismatches, duplicates); - each per-family manifest is well-formed (name/skills/description) in addition to the existing symlink-vs-frontmatter check. Also trigger the hook on .claude-plugin/plugin.json and hooks/check-upgrade.sh changes. --- .pre-commit-config.yaml | 18 +++--- tools/dev/check-family-plugins.py | 103 ++++++++++++++++++++++++------ 2 files changed, 94 insertions(+), 27 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 61978011e..a6699c3d4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -233,19 +233,21 @@ repos: files: ^(\.gitattributes|\.(agents|claude|github|kiro)/skills/.*|skills/.*|projects/_template/.*)$ types_or: [symlink, file] pass_filenames: false - # Family-plugin consistency guard. The per-family marketplace plugins under - # plugins/magpie-/ must stay in sync with the skills' `family:` - # frontmatter (the source of truth): each family's skills present as - # single-hop symlinks into the shared skills/ tree, and each family listed in - # .claude-plugin/marketplace.json alongside the all-in-one `magpie`. Catches a - # new skill, a changed family, or a stale symlink. `--fix` regenerates them. + # Marketplace-plugin consistency guard. Validates that every plugin is + # properly defined and in sync with the skills' `family:` frontmatter (the + # source of truth): the all-in-one `magpie` manifest + its upgrade-hook + # script, each per-family manifest (name / skills / description), every + # `.claude-plugin/marketplace.json` entry resolving to a matching, uniquely + # named plugin.json, and each family's skills present as single-hop symlinks + # into the shared skills/ tree. Catches a new skill, a changed family, a stale + # symlink, or a malformed manifest. `--fix` regenerates the family plugins. - repo: local hooks: - id: check-family-plugins - name: check-family-plugins (marketplace families vs frontmatter) + name: check-family-plugins (marketplace plugins vs frontmatter) language: system entry: python3 tools/dev/check-family-plugins.py - files: ^(skills/.*/SKILL\.md|plugins/.*|\.claude-plugin/marketplace\.json)$ + files: ^(skills/.*/SKILL\.md|plugins/.*|\.claude-plugin/(marketplace|plugin)\.json|hooks/check-upgrade\.sh)$ pass_filenames: false # Workspace-membership guard. The single source of truth for # which Python projects get pre-commit hooks + the CI pytest diff --git a/tools/dev/check-family-plugins.py b/tools/dev/check-family-plugins.py index f0096ac4d..b81b6d4ce 100644 --- a/tools/dev/check-family-plugins.py +++ b/tools/dev/check-family-plugins.py @@ -1,15 +1,23 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: Apache-2.0 # https://www.apache.org/licenses/LICENSE-2.0 -"""Validate the per-family marketplace plugins against the skills' `family:` -frontmatter — the source of truth. +"""Validate the marketplace plugins against the skills' `family:` frontmatter — +the source of truth. -For every family that skills declare in their `family:` frontmatter there must -be a `plugins/magpie-/` plugin whose `skills/` directory contains -exactly that family's skills as single-hop symlinks into the shared -`skills/` tree, and `.claude-plugin/marketplace.json` must list it (plus -the all-in-one `magpie`). Drift — a new skill, a changed family, a stale -symlink — fails the check. +Checks that every plugin is properly defined: + +- the all-in-one `magpie` manifest (`.claude-plugin/plugin.json`) names itself + correctly, declares `skills: ./skills`, and wires the `hooks/check-upgrade.sh` + SessionStart hook (which must exist); +- every `.claude-plugin/marketplace.json` entry resolves to a matching, + uniquely-named `plugin.json`; +- for every family declared in a skill's `family:` frontmatter there is a + `plugins/magpie-/` plugin whose manifest is well-formed and whose + `skills/` directory contains exactly that family's skills as single-hop + symlinks into the shared `skills/` tree. + +Drift — a new skill, a changed family, a stale symlink, a malformed or +mis-named manifest, a dangling marketplace entry — fails the check. Run with `--fix` to regenerate the family plugins + symlinks + marketplace entries from the frontmatter. @@ -26,6 +34,8 @@ SKILLS = Path("skills") PLUGINS = Path("plugins") MARKETPLACE = Path(".claude-plugin/marketplace.json") +ROOT_MANIFEST = Path(".claude-plugin/plugin.json") # the all-in-one `magpie` plugin +HOOK_SCRIPT = Path("hooks/check-upgrade.sh") # referenced by the all-in-one plugin hook SYMLINK_TARGET = "../../../skills/{skill}" # relative to plugins/magpie-/skills/ # Per-family descriptions used when (re)generating manifests. Keep in sync with @@ -44,6 +54,31 @@ } +def load_json(path: Path): + try: + return json.loads(path.read_text(encoding="utf-8")), None + except (OSError, ValueError) as exc: + return None, f"{path}: cannot read/parse ({exc})" + + +def validate_manifest(path: Path, expected_name: str) -> list[str]: + """A plugin.json must exist, be valid JSON, name itself correctly, and + declare its skills + a description.""" + if not path.is_file(): + return [f"{path}: missing plugin manifest"] + data, err = load_json(path) + if err: + return [err] + errors: list[str] = [] + if data.get("name") != expected_name: + errors.append(f"{path}: name is {data.get('name')!r}, expected {expected_name!r}") + if data.get("skills") != "./skills": + errors.append(f"{path}: 'skills' is {data.get('skills')!r}, expected './skills'") + if not str(data.get("description", "")).strip(): + errors.append(f"{path}: missing/empty 'description'") + return errors + + def families_from_frontmatter() -> dict[str, set[str]]: fam: dict[str, set[str]] = {} for path in sorted(SKILLS.glob("*/SKILL.md")): @@ -56,31 +91,60 @@ def families_from_frontmatter() -> dict[str, set[str]]: def check(fam: dict[str, set[str]]) -> list[str]: errors: list[str] = [] - try: - market = json.loads(MARKETPLACE.read_text(encoding="utf-8")) - listed = {p["name"] for p in market.get("plugins", [])} - except (OSError, ValueError) as exc: - return [f"{MARKETPLACE}: cannot read/parse ({exc})"] + market, err = load_json(MARKETPLACE) + if err: + return [err] + entries = market.get("plugins", []) + listed = {p.get("name") for p in entries} + # 1) The all-in-one `magpie` plugin: well-formed manifest, listed, and its + # SessionStart hook script is present + referenced. + errors += validate_manifest(ROOT_MANIFEST, "magpie") if "magpie" not in listed: errors.append(f"{MARKETPLACE}: missing the all-in-one 'magpie' plugin entry") + if not HOOK_SCRIPT.is_file(): + errors.append(f"{HOOK_SCRIPT}: missing (referenced by the all-in-one plugin's SessionStart hook)") + root_data, root_err = load_json(ROOT_MANIFEST) + if root_data is not None and "check-upgrade.sh" not in json.dumps(root_data.get("hooks", {})): + errors.append(f"{ROOT_MANIFEST}: SessionStart hook does not reference hooks/check-upgrade.sh") + + # 2) Every marketplace entry resolves to a matching, uniquely-named manifest. + seen: set[str] = set() + for ent in entries: + name = ent.get("name") + source = ent.get("source") + if not name or not source: + errors.append(f"{MARKETPLACE}: entry missing 'name'/'source': {ent}") + continue + if name in seen: + errors.append(f"{MARKETPLACE}: duplicate plugin entry '{name}'") + seen.add(name) + manifest = ROOT_MANIFEST if source == "." else Path(source) / ".claude-plugin" / "plugin.json" + if not manifest.is_file(): + errors.append(f"{MARKETPLACE}: '{name}' source '{source}' has no {manifest}") + continue + data, jerr = load_json(manifest) + if jerr is None and data.get("name") != name: + errors.append(f"{MARKETPLACE}: '{name}' resolves to plugin.json named {data.get('name')!r}") + # 3) Per-family plugins: manifest well-formed + symlinks match the frontmatter. for family, skills in sorted(fam.items()): name = f"magpie-{family}" pdir = PLUGINS / name - if not (pdir / ".claude-plugin" / "plugin.json").is_file(): - errors.append(f"{name}: missing {pdir}/.claude-plugin/plugin.json") + manifest_errs = validate_manifest(pdir / ".claude-plugin" / "plugin.json", name) + errors += manifest_errs + if manifest_errs: continue if name not in listed: errors.append(f"{MARKETPLACE}: missing entry for '{name}'") sdir = pdir / "skills" have: dict[str, Path] = {} - for entry in sorted(sdir.iterdir()) if sdir.is_dir() else []: - if entry.is_symlink(): - have[entry.name] = entry.readlink() + for link in sorted(sdir.iterdir()) if sdir.is_dir() else []: + if link.is_symlink(): + have[link.name] = link.readlink() else: - errors.append(f"{name}: {entry} is not a symlink") + errors.append(f"{name}: {link} is not a symlink") for skill in sorted(skills - set(have)): errors.append(f"{name}: missing symlink for '{skill}' (family={family})") @@ -91,6 +155,7 @@ def check(fam: dict[str, set[str]]) -> list[str]: if have[skill] != want: errors.append(f"{name}: {sdir / skill} -> {have[skill]} (expected {want})") + # 4) No orphan plugin dirs (a magpie- with no skills declaring family x). for pdir in sorted(PLUGINS.glob("magpie-*")): family = pdir.name[len("magpie-"):] if family not in fam: