Files
openprose__prose/scripts/sync-copy.sh
Jose Montes de Oca d43b85157b feat(plugin): standardize marketplace manifests and submission docs (#56)
The `open-prose` plugin ships to two marketplaces (Claude Code and Codex) via three manifest surfaces with overlapping but drifted metadata. This change unifies copy across those surfaces behind a single declarative source, ships visual assets for the Codex `interface` block, gates drift in CI, and adds a maintainer-facing submission playbook to `RELEASE.md`.

## Changes

- **Copy sync** — `.plugin-meta.json` declares the canonical strings; `scripts/sync-copy.sh` writes them into every declared destination (mirrors `bump-version.sh`). `--check` mode asserts byte-equality and reports drift.
- **Unified manifests** — same descriptions across `.claude-plugin/`, `.codex-plugin/`, and `.agents/plugins/`. Registry name aligned to `openprose` (plugin name `open-prose` unchanged). Codex `skills` path normalized.
- **Visual assets** — `assets/plugin/logo.png` (512×512) and `assets/plugin/composer-icon.png` (64×64), wired into the Codex `interface` block with `brandColor` (`#8a6b2e`), `websiteURL`, and the icon slots.
- **CI gates** — four new steps in `.github/workflows/plugin-manifest.yml` covering copy sync, asset presence, Codex interface completeness, and unified registry names. Existing version-sync gate untouched.
- **Submission playbook** — new `## Marketplace submission` section in `RELEASE.md` covering Claude Code (live today) and the Codex Plugin Directory (curated; self-serve coming soon).
2026-05-04 12:14:59 -07:00

80 lines
2.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Sync canonical plugin copy across declared manifest files.
#
# Usage:
# ./scripts/sync-copy.sh # write canonical copy into every declared destination
# ./scripts/sync-copy.sh --check # exit 0 iff every destination already matches
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CONFIG="$REPO_ROOT/.plugin-meta.json"
if [[ ! -f "$CONFIG" ]]; then
echo "error: $CONFIG not found" >&2
exit 2
fi
mode="${1:-write}"
pointer_to_jq() {
# "interface.shortDescription" -> ".interface.shortDescription"
# "plugins[0].description" -> ".plugins[0].description"
printf '.%s' "$1"
}
read_field() {
local path="$1" jq_expr="$2"
jq -r "$jq_expr" "$REPO_ROOT/$path"
}
write_field() {
local path="$1" jq_expr="$2" value="$3"
local tmp
tmp="$(mktemp)"
jq --arg v "$value" "${jq_expr} = \$v" "$REPO_ROOT/$path" > "$tmp"
mv "$tmp" "$REPO_ROOT/$path"
}
slots=(tagline shortDescription longDescription)
case "$mode" in
--check)
drift=0
for slot in "${slots[@]}"; do
expected="$(jq -r ".${slot}" "$CONFIG")"
while IFS= read -r entry; do
path="$(jq -r '.path' <<<"$entry")"
pointer="$(jq -r '.pointer' <<<"$entry")"
jq_expr="$(pointer_to_jq "$pointer")"
actual="$(read_field "$path" "$jq_expr")"
if [[ "$actual" != "$expected" ]]; then
echo "drift: $path:$pointer" >&2
echo " expected: $expected" >&2
echo " actual: $actual" >&2
drift=1
fi
done < <(jq -c ".targets.${slot}[]" "$CONFIG")
done
if [[ $drift -ne 0 ]]; then
exit 1
fi
echo "ok: all declared destinations in sync"
;;
write)
for slot in "${slots[@]}"; do
value="$(jq -r ".${slot}" "$CONFIG")"
while IFS= read -r entry; do
path="$(jq -r '.path' <<<"$entry")"
pointer="$(jq -r '.pointer' <<<"$entry")"
jq_expr="$(pointer_to_jq "$pointer")"
write_field "$path" "$jq_expr" "$value"
echo "wrote $path:$pointer"
done < <(jq -c ".targets.${slot}[]" "$CONFIG")
done
;;
*)
echo "usage: $0 [--check]" >&2
exit 2
;;
esac