fix(meta): 補齊稽核缺失並修掉護欄失效

What:依 jsc-meta:skill-check 的稽核結果修正技能與工具——補上每個步驟的可檢核完成條件、
把留在內文的標準輸入輸出流程下放 tools/、修正查表與退碼路由造成的誤判。

Why:稽核發現這些缺失會讓技能在實際執行時走錯分支或靜默通過。
完成條件缺漏是最常被違反的一項;退碼誤判與查表錯誤則會讓良性狀況被當成失敗。

How:逐項對照 references/guidelines.md 的審核檢查清單修正,新增的工具都有
documented exit codes,並以真實執行驗證每條路徑。

Who:jsc-meta:skill-check 例行稽核(2026-08-25)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 14:58:54 +08:00
co-authored by Claude Opus 5
parent 287e3ff113
commit 9ab5b42864
12 changed files with 490 additions and 51 deletions
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env sh
# sync-domains.sh — 依 Gitea 正本 marketplace 把所有 domain 存取庫同步到本機。
#
# 用法: sync-domains.sh(不吃參數)
#
# 流程:
# 1. 透過 jsc-gitea/tools/gitea.sh 讀正本 marketplace
# (plugins/meta 的 .claude-plugin/marketplace.json),取得權威 domain 清單。
# 2. 本機缺少的 domain:用 gitea.sh clone-url plugins/{domain} 取網址後 clone。
# 3. 本機已有的 domain:git pull。工作區有未提交變更就跳過 pull,只在 stderr 提醒——
# 正在改的 repo 不該被自動 pull 覆蓋。
#
# 根目錄: 預設取本腳本位置的上兩層(meta/tools -> meta -> 根)。
# 用 JSC_PLUGINS_ROOT 覆寫,換機器不必改腳本。
# 存取庫目錄名: 先找 {root}/{domain},再找 {root}/jsc-{domain};都沒有才 clone 成 {root}/{domain}。
#
# 輸出: 一行一個 domain,格式 {domain}<TAB>{path}(stdout);警告與失敗說明走 stderr。
# 結束碼: 0=正本讀到、每個 domain 都在本機,而且每個既有存取庫都更新到最新
# 1=讀不到正本 marketplace 或找不到 gitea.sh(不輸出任何 domain)
# 2=正本讀到,但有 domain 沒能取得或 clone 失敗(已輸出其餘 domain)
# 3=每個 domain 都在本機,但有存取庫沒更新到最新:工作區脏而跳過 pull,
# 或 pull 失敗。stderr 會逐一列出這些路徑。**exit 0 才代表「全部最新」**;
# 拿 3 當乾淨會讓後續步驟改在落後的檔案上。
# 同時發生時回較嚴重的那個:2 蓋過 3。
# 環境變數: JSC_PLUGINS_ROOT(根目錄)
# JSC_SYNC_DRY_RUN=1(只印出會做什麼,不動檔案;不執行 pull/clone,
# 所以也不因為「沒更新」回 3)
set -u
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
ROOT="${JSC_PLUGINS_ROOT:-$(CDPATH= cd -- "$HERE/../.." && pwd)}"
DRY="${JSC_SYNC_DRY_RUN:-}"
[ -d "$ROOT" ] || { echo "找不到 plugins 根目錄:$ROOT(可用 JSC_PLUGINS_ROOT 指定)" >&2; exit 1; }
GITEA=""
for g in "$ROOT/gitea/tools/gitea.sh" "$ROOT/jsc-gitea/tools/gitea.sh"; do
[ -f "$g" ] && { GITEA="$g"; break; }
done
[ -n "$GITEA" ] || { echo "找不到 gitea.sh:請先取得 jsc-gitea 存取庫" >&2; exit 1; }
mkt=$(sh "$GITEA" api GET /repos/plugins/meta/raw/.claude-plugin/marketplace.json 2>/dev/null) || mkt=""
[ -n "$mkt" ] || { echo "讀不到正本 marketplace:檢查 GITEA_HOST、GITEA_TOKEN 或 tea 登入" >&2; exit 1; }
DOMAINS=$(printf '%s\n' "$mkt" | sed -n 's/.*"name": *"jsc-\([a-z0-9-]*\)".*/\1/p' | sort -u)
[ -n "$DOMAINS" ] || { echo "正本 marketplace 裡沒有 jsc-{domain} 條目" >&2; exit 1; }
repo_path() { # $1=domain;印出既有路徑(return 0)或預設路徑(return 1)
for d in "$ROOT/$1" "$ROOT/jsc-$1"; do
[ -d "$d" ] && { printf '%s' "$d"; return 0; }
done
printf '%s' "$ROOT/$1"
return 1
}
rc=0
stale=0
for domain in $DOMAINS; do
if path=$(repo_path "$domain"); then
if [ -n "$(git -C "$path" status --porcelain 2>/dev/null)" ]; then
echo "工作區有未提交變更,跳過 pull,未更新到最新:$path" >&2
stale=1
elif [ "$DRY" = 1 ]; then
echo "[dry-run] git -C $path pull --ff-only" >&2
elif ! git -C "$path" pull --quiet --ff-only >/dev/null 2>&1; then
echo "pull 失敗,保留本機版本,未更新到最新:$path" >&2
stale=1
fi
else
url=$(sh "$GITEA" clone-url "plugins/$domain" 2>/dev/null) || url=""
if [ -z "$url" ]; then
echo "取不到 clone 網址,略過:plugins/$domain" >&2
rc=2
continue
fi
if [ "$DRY" = 1 ]; then
echo "[dry-run] git clone $url $path" >&2
elif ! git clone --quiet "$url" "$path" >/dev/null 2>&1; then
echo "clone 失敗:$url" >&2
rc=2
continue
fi
fi
printf '%s\t%s\n' "$domain" "$path"
done
# 2(取不到或 clone 失敗)比 3(沒更新到最新)嚴重,優先回報。
[ "$rc" -eq 0 ] && [ "$stale" -eq 1 ] && rc=3
exit $rc