mirror of
https://github.com/openprose/prose.git
synced 2026-09-19 05:55:05 +08:00
d43b85157b
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).
80 lines
2.1 KiB
Bash
Executable File
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
|