fix(kyberforge): keep .github/plugin/marketplace.json synced

apm.yml only declares claude and codex marketplace output profiles --
codex writes a differently-shaped file to .agents/plugins/marketplace.json,
so nothing regenerates the legacy Copilot CLI path at
.github/plugin/marketplace.json. It was hand-synced once during the #90
conversion and had drifted (missing the category field added when codex
output was enabled) with no gate to catch it.

scripts/sync-marketplace-mirror.sh keeps it byte-identical to the compiled
.claude-plugin/marketplace.json, wired as a pre-push check so it can't go
stale silently again.

Refs: #90
This commit is contained in:
2026-08-13 18:54:10 +00:00
parent a8beff7d2c
commit a873e93050
4 changed files with 62 additions and 1 deletions

View File

@@ -7,36 +7,42 @@
}, },
"plugins": [ "plugins": [
{ {
"category": "Developer Tools",
"description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.", "description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.",
"name": "kyberforge", "name": "kyberforge",
"source": "./plugins/kyberforge", "source": "./plugins/kyberforge",
"version": "1.4.0" "version": "1.4.0"
}, },
{ {
"category": "Utilities",
"description": "A place for things to be binned", "description": "A place for things to be binned",
"name": "bin", "name": "bin",
"source": "./plugins/bin", "source": "./plugins/bin",
"version": "1.1.1" "version": "1.1.1"
}, },
{ {
"category": "Version Control",
"description": "Skills for working with Git \u2014 conventional commits, branch management, pull requests, and feature flow.", "description": "Skills for working with Git \u2014 conventional commits, branch management, pull requests, and feature flow.",
"name": "git", "name": "git",
"source": "./plugins/git", "source": "./plugins/git",
"version": "1.3.2" "version": "1.3.2"
}, },
{ {
"category": "Version Control",
"description": "Skills for managing Gitea repositories \u2014 issues, pull requests, milestones, releases, and wikis.", "description": "Skills for managing Gitea repositories \u2014 issues, pull requests, milestones, releases, and wikis.",
"name": "gitea", "name": "gitea",
"source": "./plugins/gitea", "source": "./plugins/gitea",
"version": "1.3.3" "version": "1.3.3"
}, },
{ {
"category": "Productivity",
"description": "Cross-cutting utility skills for everyday AI-assisted coding \u2014 triage, diagnosis, architecture review, and session navigation.", "description": "Cross-cutting utility skills for everyday AI-assisted coding \u2014 triage, diagnosis, architecture review, and session navigation.",
"name": "core", "name": "core",
"source": "./plugins/core", "source": "./plugins/core",
"version": "1.1.0" "version": "1.1.0"
}, },
{ {
"category": "Productivity",
"description": "Skills for Real Engineers \u2014 planning, TDD, architecture, and debugging workflows from Matt Pocock's .claude directory.", "description": "Skills for Real Engineers \u2014 planning, TDD, architecture, and debugging workflows from Matt Pocock's .claude directory.",
"name": "mattpocock-skills", "name": "mattpocock-skills",
"source": { "source": {
@@ -48,6 +54,7 @@
} }
}, },
{ {
"category": "Developer Tools",
"description": "Skills and agents for configuring and running linters.", "description": "Skills and agents for configuring and running linters.",
"name": "lint", "name": "lint",
"source": "./plugins/lint", "source": "./plugins/lint",

View File

@@ -71,6 +71,15 @@ repos:
pass_filenames: false pass_filenames: false
always_run: true always_run: true
- id: check-marketplace-mirror-sync
name: Check marketplace mirror sync
description: Verify .github/plugin/marketplace.json (Copilot CLI's legacy manifest path) is byte-identical to .claude-plugin/marketplace.json -- apm has no output profile for this path, so it must be kept in sync explicitly (see issue #90)
entry: bash scripts/sync-marketplace-mirror.sh --check
language: system
stages: [pre-push]
pass_filenames: false
always_run: true
- id: apm-marketplace-check - id: apm-marketplace-check
name: apm marketplace check name: apm marketplace check
description: Validate every marketplace.packages[] entry resolves, including network reachability of remote refs -- catches stale/unreachable remote package references that check-manifests.sh deliberately skips (local-source checks only) description: Validate every marketplace.packages[] entry resolves, including network reachability of remote refs -- catches stale/unreachable remote package references that check-manifests.sh deliberately skips (local-source checks only)

View File

@@ -30,7 +30,7 @@ Reusable slash commands for AI coding tools, defined as `SKILL.md` files followi
The deployable unit in the plugin marketplace. A plugin bundles one or more skills, agents, hooks, prompts, MCP servers, and optionally a `bin/` directory into a single installable directory. In this repo, plugins live under `plugins/<name>/`, each with its own `apm.yml` + `.apm/{skills,agents,hooks,...}` — this is the authoring source of truth for the plugin's content (ADR-0015). Two categories of tracked output are compiled from that source, never hand-edited: `.claude-plugin/plugin.json` (Claude Code) and `.github/plugin/plugin.json` (Copilot CLI) via `apm pack`/`apm compile`; and, alongside them, a flat `agents/`, `skills/`, `commands/`, `instructions/`, `extensions/` directory mirror plus a merged `hooks.json` at the plugin root, generated by `scripts/sync-plugin-content.sh` — Claude Code's and Copilot's installers convention-scan only these flat root-level paths and have no awareness of `.apm/` nesting at all, so this mirror is what actually makes `.apm/` content discoverable at install time (ADR-0017). Plugins are copied to a cache on install — they cannot reference files outside their own directory. Install a plugin with `claude plugin install <name>@<marketplace>`. The deployable unit in the plugin marketplace. A plugin bundles one or more skills, agents, hooks, prompts, MCP servers, and optionally a `bin/` directory into a single installable directory. In this repo, plugins live under `plugins/<name>/`, each with its own `apm.yml` + `.apm/{skills,agents,hooks,...}` — this is the authoring source of truth for the plugin's content (ADR-0015). Two categories of tracked output are compiled from that source, never hand-edited: `.claude-plugin/plugin.json` (Claude Code) and `.github/plugin/plugin.json` (Copilot CLI) via `apm pack`/`apm compile`; and, alongside them, a flat `agents/`, `skills/`, `commands/`, `instructions/`, `extensions/` directory mirror plus a merged `hooks.json` at the plugin root, generated by `scripts/sync-plugin-content.sh` — Claude Code's and Copilot's installers convention-scan only these flat root-level paths and have no awareness of `.apm/` nesting at all, so this mirror is what actually makes `.apm/` content discoverable at install time (ADR-0017). Plugins are copied to a cache on install — they cannot reference files outside their own directory. Install a plugin with `claude plugin install <name>@<marketplace>`.
### Plugin marketplace ### Plugin marketplace
A Git repository with a `marketplace.json` manifest listing installable plugins. No backend, registry, or SaaS required — the Git repo is the marketplace. This repo is the `holocron` marketplace. The manifest at `.claude-plugin/marketplace.json` (read by both Claude Code and Copilot CLI, mirrored to `.github/plugin/marketplace.json`) is **compiled output** of `apm pack`, generated from the root `apm.yml`'s `marketplace:` block (owner, build/output config, versioning strategy, and the `packages:` list of installable plugins) — it is not hand-edited. See ADR-0015. Each listed package's `source:` still points at that plugin's own `plugins/<name>/` root, not at an `apm pack` build artifact — which is why that root also carries the flat `agents/`/`skills/`/`commands/`/`hooks.json` content mirror described under "Plugin" (ADR-0017): without it, an install from this marketplace finds a valid manifest but no discoverable content. A Git repository with a `marketplace.json` manifest listing installable plugins. No backend, registry, or SaaS required — the Git repo is the marketplace. This repo is the `holocron` marketplace. The manifest at `.claude-plugin/marketplace.json` (read by both Claude Code and Copilot CLI) is **compiled output** of `apm pack`, generated from the root `apm.yml`'s `marketplace:` block (owner, build/output config, versioning strategy, and the `packages:` list of installable plugins) — it is not hand-edited. See ADR-0015. `.github/plugin/marketplace.json` is Copilot CLI's legacy manifest path; apm has no output profile for it (only `claude` and `codex`, and `codex`'s is a differently-shaped file at `.agents/plugins/marketplace.json`), so `scripts/sync-marketplace-mirror.sh` keeps it byte-identical to `.claude-plugin/marketplace.json`, checked at pre-push. Each listed package's `source:` still points at that plugin's own `plugins/<name>/` root, not at an `apm pack` build artifact — which is why that root also carries the flat `agents/`/`skills/`/`commands/`/`hooks.json` content mirror described under "Plugin" (ADR-0017): without it, an install from this marketplace finds a valid manifest but no discoverable content.
### HITL (human-in-the-loop) ### HITL (human-in-the-loop)
Agent pauses before a consequential action; human approves before execution. Required for irreversible or high-stakes actions (architecture changes, production deployments, security configuration). The agent drafts the change plan and waits — it does not proceed autonomously. Contrast with HOTL. Agent pauses before a consequential action; human approves before execution. Required for irreversible or high-stakes actions (architecture changes, production deployments, security configuration). The agent drafts the change plan and waits — it does not proceed autonomously. Contrast with HOTL.

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -euo pipefail
# .claude-plugin/marketplace.json is apm's compiled Claude marketplace output (see
# apm.yml's marketplace.outputs.claude). GitHub Copilot CLI's manifest lookup accepts
# that same file at .claude-plugin/marketplace.json directly, but also has a legacy
# convention path at .github/plugin/marketplace.json (see
# plugins/kyberforge/docs/research/docs/github-copilot-plugins/marketplace.md) -- and
# CONTEXT.md documents that path as a mirror of the Claude output, not a separate apm
# output profile (apm only ships "claude" and "codex" mappers; codex writes a
# differently-shaped file to .agents/plugins/marketplace.json, not this path). This
# script keeps that legacy mirror byte-identical to .claude-plugin/marketplace.json
# instead of letting it silently drift (see issue #90 comment thread).
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
SRC="$REPO_ROOT/.claude-plugin/marketplace.json"
DST="$REPO_ROOT/.github/plugin/marketplace.json"
usage() {
echo "Usage: $0 [--check]" >&2
exit 1
}
CHECK=0
if [[ "${1:-}" == "--check" ]]; then
CHECK=1
shift
fi
[[ $# -eq 0 ]] || usage
if [[ ! -f "$SRC" ]]; then
exit 0
fi
if [[ "$CHECK" -eq 1 ]]; then
if [[ ! -f "$DST" ]] || ! diff -q "$SRC" "$DST" >/dev/null 2>&1; then
echo "DRIFT $DST: out of sync with .claude-plugin/marketplace.json" >&2
echo "Fix: bash scripts/sync-marketplace-mirror.sh" >&2
exit 1
fi
exit 0
fi
mkdir -p "$(dirname "$DST")"
cp "$SRC" "$DST"