diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json index 816db4d..eafc041 100644 --- a/.github/plugin/marketplace.json +++ b/.github/plugin/marketplace.json @@ -7,36 +7,42 @@ }, "plugins": [ { + "category": "Developer Tools", "description": "Skills and agents for creating, maintaining, and managing a Claude Code / Copilot CLI plugin marketplace.", "name": "kyberforge", "source": "./plugins/kyberforge", "version": "1.4.0" }, { + "category": "Utilities", "description": "A place for things to be binned", "name": "bin", "source": "./plugins/bin", "version": "1.1.1" }, { + "category": "Version Control", "description": "Skills for working with Git \u2014 conventional commits, branch management, pull requests, and feature flow.", "name": "git", "source": "./plugins/git", "version": "1.3.2" }, { + "category": "Version Control", "description": "Skills for managing Gitea repositories \u2014 issues, pull requests, milestones, releases, and wikis.", "name": "gitea", "source": "./plugins/gitea", "version": "1.3.3" }, { + "category": "Productivity", "description": "Cross-cutting utility skills for everyday AI-assisted coding \u2014 triage, diagnosis, architecture review, and session navigation.", "name": "core", "source": "./plugins/core", "version": "1.1.0" }, { + "category": "Productivity", "description": "Skills for Real Engineers \u2014 planning, TDD, architecture, and debugging workflows from Matt Pocock's .claude directory.", "name": "mattpocock-skills", "source": { @@ -48,6 +54,7 @@ } }, { + "category": "Developer Tools", "description": "Skills and agents for configuring and running linters.", "name": "lint", "source": "./plugins/lint", diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc1752e..7a11ad2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -71,6 +71,15 @@ repos: pass_filenames: false 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 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) diff --git a/CONTEXT.md b/CONTEXT.md index f5b5492..1569c62 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -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//`, 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 @`. ### 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//` 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//` 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) 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. diff --git a/scripts/sync-marketplace-mirror.sh b/scripts/sync-marketplace-mirror.sh new file mode 100755 index 0000000..07d60e4 --- /dev/null +++ b/scripts/sync-marketplace-mirror.sh @@ -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"