ktg-plugin-marketplace/docs/marketplace-polyrepo-migration/migration/70-thin-catalog.sh
Kjell Tore Guttormsen 3a5f558a48 chore(migration): catalog-thinning script (staged for window)
70-thin-catalog.sh: renders the thin-catalog end-state into --workspace (never the live tree). git-archive HEAD -> remove plugins/ + shared/ + scripts/sync-design-system.mjs(+test) -> extract CLAUDE.md conventions into CONVENTIONS.md (D6) -> thin the catalog CLAUDE.md -> rewrite README.md with external Forgejo repo links and versions re-stated from plugin-map.json (M15 coupled to the D6 rewrite).

Verify: workspace has no plugins/ or shared/, CONVENTIONS.md present; rewritten README has 0 local links and corrects the stale playground-design-system v0.1 -> v0.6.0. Test 4/4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 13:38:42 +02:00

135 lines
5.7 KiB
Bash

#!/usr/bin/env bash
# Step 9 — catalog-thinning script (staged for the operator window).
#
# Renders the THIN-CATALOG end-state into a --workspace. NEVER mutates the live tree
# (forbidden_paths: plugins, shared, CLAUDE.md, README.md) — every write lands inside --workspace.
# After the operator window the catalog repo hosts only the marketplace manifest + the catalog-level
# docs; the 10 plugins + the design-system live in their own Forgejo repos under
# https://git.fromaitochitta.com/open/. This script produces that state for inspection/staging:
#
# 1. archives the tracked catalog (git archive HEAD) into --workspace (clean: no .git, no untracked)
# 2. removes plugins/, shared/, scripts/sync-design-system.mjs (+ its test)
# 3. extracts the marketplace conventions from CLAUDE.md into CONVENTIONS.md (D6)
# 4. thins the catalog CLAUDE.md to catalog-maintenance only
# 5. rewrites the landing README.md to point at the external plugin repos, re-stating every version
# from plugin-map.json (the verified source). M15 is COUPLED to this D6 rewrite, not a ride-along:
# a roster that re-states versions/counts must state the verified ones or it ships a false document.
#
# NULL push (D8): writes only inside --workspace.
#
# Usage: 70-thin-catalog.sh --workspace <dir>
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
MAP="$SCRIPT_DIR/plugin-map.json"
WS=""
while [ $# -gt 0 ]; do
case "$1" in
--workspace) WS="${2:-}"; shift 2 ;;
*) echo "unknown arg: $1" >&2; exit 2 ;;
esac
done
[ -n "$WS" ] || { echo "usage: 70-thin-catalog.sh --workspace <dir>" >&2; exit 2; }
[ -f "$MAP" ] || { echo "plugin-map.json missing at $MAP" >&2; exit 1; }
# --- 1. Archive the tracked catalog into the workspace (no .git, no untracked) ---
rm -rf "$WS"
mkdir -p "$WS"
git -C "$REPO_ROOT" archive HEAD | tar -x -C "$WS"
# --- 2. Remove the components that move out to their own repos ---
rm -rf "$WS/plugins" "$WS/shared" \
"$WS/scripts/sync-design-system.mjs" "$WS/scripts/sync-design-system.test.mjs"
rmdir "$WS/scripts" 2>/dev/null || true
# --- 3. Extract conventions -> CONVENTIONS.md (D6) ---
{
echo "# Conventions — ktg-plugin-marketplace catalog"
echo
echo "> Extracted from the marketplace CLAUDE.md (D6). These are the marketplace-wide conventions"
echo "> every plugin repo inherits under fork-and-own. See GOVERNANCE.md for the governance model."
echo
awk '
/^## Konvensjoner/ { f=1; print; next }
f && /^## / { exit }
f { print }
' "$WS/CLAUDE.md"
} > "$WS/CONVENTIONS.md"
# --- 4. Thin the catalog CLAUDE.md to catalog-maintenance only ---
cat > "$WS/CLAUDE.md" <<'MD'
# ktg-plugin-marketplace (catalog)
Catalog repository for the ktg-plugin-marketplace. After the polyrepo migration this repo hosts only
the marketplace manifest and the catalog-level docs; every plugin and the shared design-system live in
their own Forgejo repositories under `https://git.fromaitochitta.com/open/`.
## What lives here
- `.claude-plugin/marketplace.json` — the marketplace manifest (plugin entries point at external repos)
- `README.md` — the landing/catalog page
- `CONVENTIONS.md` — marketplace-wide conventions inherited by every plugin repo
- `GOVERNANCE.md` — governance + fork-and-own model
- `.mailmap`, `.gitleaks.toml`, `.gitleaksignore` — shared git-hygiene baselines
## Catalog maintenance
- Marketplace conventions: see CONVENTIONS.md.
- Adding/updating a plugin entry: edit `.claude-plugin/marketplace.json` (external `source: "url"` with
a pinned `ref`) and re-state the plugin in README.md with its verified version.
- Plugin source, issues, and releases live in each plugin's own repository — not here.
MD
# --- 5. Rewrite the landing README.md: external repo links + versions re-stated from plugin-map.json ---
python3 - "$MAP" "$WS/README.md" <<'PY'
import json, re, sys
map_path, readme_path = sys.argv[1], sys.argv[2]
targets = json.load(open(map_path))["targets"]
def browse(key):
u = targets[key]["repo_url"]
return u[:-4] if u.endswith(".git") else u
tag = {k: targets[k].get("tag", "") for k in targets}
# shared/playground-examples/ is bundled into the playground-design-system repo (plugin-map path_renames)
EXAMPLES_OWNER = "playground-design-system"
src = open(readme_path).read()
def link_repl(m):
container, key, sub = m.group(1), m.group(2), (m.group(3) or "")
sub = sub.lstrip("/")
if container == "shared" and key == "playground-examples":
owner = EXAMPLES_OWNER
subpath = ("playground-examples/" + sub) if sub else ""
elif key in targets:
owner = key
subpath = sub
else:
return m.group(0) # unknown — leave as-is (surfaced by the no-local-links assertion)
base = browse(owner)
if not subpath or subpath == "README.md":
return "](%s)" % base
return "](%s/src/branch/main/%s)" % (base, subpath)
# swap every local ](plugins/<key>/...) / ](shared/<key>/...) link to its external repo
src = re.sub(r"\]\((plugins|shared)/([a-z0-9-]+)(/[^)]*)?\)", link_repl, src)
# re-state the version backtick on heading lines that now link to a known external repo
def fix_version(line):
m = re.search(r"\]\(https://git\.fromaitochitta\.com/open/([a-z0-9-]+)\)", line)
if m and m.group(1) in tag and tag[m.group(1)]:
line = re.sub(r"`v[0-9][^`]*`", "`%s`" % tag[m.group(1)], line, count=1)
return line
out = "\n".join(fix_version(l) for l in src.split("\n"))
open(readme_path, "w").write(out)
PY
echo "thin-catalog ready at $WS"
echo " removed: plugins/ shared/ scripts/sync-design-system.mjs"
echo " added: CONVENTIONS.md (extracted from CLAUDE.md conventions)"
echo " README.md rewritten: external repo links + versions re-stated from plugin-map.json"