feat(skills): 新增技能盤點頁與共用部署驗證流程,並把技能驗證移到新行程
技能盤點以前只回到對話裡,換一台機器就得重跑才知道裝了什麼。 現在新增技能盤點這個 wiki 頁類型,雜湊取「主機、工具名稱、登入帳號」三段。 每支 CLI 各有自己的 plugin 集合,也各有自己的 hook 接線,那是互相獨立的事實。 少了工具名稱那一段,同一台機器上五支 CLI 會算出同一個雜湊,五份盤點互相覆蓋, 讀的人還看不出被蓋掉。技能盤點新增寫入這兩頁的步驟,整步規定必須開 sub agent。 兩份樣板刻意分開:內容頁每次盤點覆寫整頁,目錄頁只更新自己那一列, 兩者的寫入語意剛好相反,合成一份遲早有人把別台機器的紀錄刪掉。 四支異動技能原本在部署完的同一個工作階段,就叫用剛做好的技能。 部署收尾自己立起重啟閘門,那支技能必被擋下,驗證做不完。 解法不是把它加進豁免清單。豁免擋得住閘門,擋不住「行程還載著舊版」這件事, 硬過關驗到的是舊版行為,等於假通過。所以把判路線、部署、驗證、失敗分流 抽成一份共用說明,驗證一律另開 CLI 行程執行,四支技能只留一行指標指過去。 新增腳本檢查工具,一次做完語法、執行權限與結束碼宣告三項檢查, 只被 source 的函式庫豁免後兩項,而且逐支記在錯誤輸出,不靜默略過。 新增部署路線判定工具,判定改動有沒有進存取庫的預設分支, 取代四支技能各抄一段、各自漂移的散文;判不出來就回報停下,不自己挑路線走。 同時把四支技能裡的中文段落抽到共用說明、指標改回英文, 修正六處相對路徑,把技能盤點的模糊描述改成查得出來的條件, 並讓 manifest 同步的每一個呼叫端逐碼分流。 七支技能改為併行執行:例行稽核從九步併成七步,技能盤點併成六步。 技能盤點不再重跑盤點腳本內部已經跑過的三支腳本, 而那三支原本兼作獨立交叉檢查,拿掉就少一層保護, 所以把少掉的是什麼、風險由誰擋住,明白寫進 Notes,不當作沒發生。
This commit is contained in:
Executable
+105
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env sh
|
||||
# lint-scripts.sh — 檢查一個 domain 存取庫的 shell 腳本:語法、可執行、結束碼有沒有記載。
|
||||
#
|
||||
# 用法: lint-scripts.sh <domain-path>
|
||||
#
|
||||
# 檢查三項(掃 {domain-path}/tools 與 {domain-path}/hooks 底下的 *.sh):
|
||||
# 1. 語法 — sh -n 通過。
|
||||
# 2. 可執行 — 檔案有執行權限。技能直接呼叫的腳本沒有 +x,會在部署後才炸。
|
||||
# 3. 結束碼 — 檔頭前 60 行要有結束碼宣告列(「結束碼:」或「exit code」)。
|
||||
# 沒有宣告的腳本,呼叫端沒辦法逐碼分流,只能猜;猜錯就把失敗當成功。
|
||||
#
|
||||
# 為什麼合成一支: skill-check 原本在 SKILL.md 裡寫 find ... -exec sh -n,另外兩項靠散文
|
||||
# 要求人工比對。三項的輸入輸出都固定,散文版每個 domain 各做一次,還會跟腳本實況漂移。
|
||||
#
|
||||
# 例外: 只被 source 的共用函式庫(判準見 is_library)第 2、3 項都不查——它不是可執行入口,
|
||||
# 沒有 +x 的必要,也沒有結束碼語意可宣告。第 1 項語法檢查照查,函式庫一樣會被 sh -n 讀。
|
||||
# 豁免不是靜默的: 每豁免一支就在 stderr 記一行,摘要也帶函式庫支數,才看得出誰被跳過。
|
||||
#
|
||||
# 輸出: 一行一個不合格項目,格式 {檔案}:{檢查項}:{說明}(stdout);
|
||||
# 函式庫豁免通知與統計摘要走 stderr。
|
||||
# 結束碼: 0=掃到腳本且三項全過
|
||||
# 1=有不合格項目(清單在 stdout)
|
||||
# 2=用法錯誤(本腳本只吃一個參數)
|
||||
# 3=domain 路徑不存在,或 tools/ 與 hooks/ 都沒有 *.sh——**什麼都沒掃**,
|
||||
# 不等於通過。缺這兩個目錄本身不是失敗,呼叫端照實記「無腳本可掃」即可。
|
||||
set -u
|
||||
|
||||
usage() {
|
||||
echo 'usage: lint-scripts.sh <domain-path>' >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
[ "$#" -eq 1 ] || usage
|
||||
DOMAIN=${1%/}
|
||||
[ -n "$DOMAIN" ] || usage
|
||||
[ -d "$DOMAIN" ] || { echo "找不到 domain 路徑:$DOMAIN" >&2; exit 3; }
|
||||
|
||||
SCAN=''
|
||||
for d in "$DOMAIN/tools" "$DOMAIN/hooks"; do
|
||||
[ -d "$d" ] && SCAN="$SCAN $d"
|
||||
done
|
||||
[ -n "$SCAN" ] || { echo "沒有 tools/ 也沒有 hooks/,無腳本可掃:$DOMAIN" >&2; exit 3; }
|
||||
|
||||
TMP=$(mktemp) || { echo "無法建立暫存檔" >&2; exit 3; }
|
||||
trap 'rm -f "$TMP"' EXIT
|
||||
|
||||
# shellcheck disable=SC2086
|
||||
find $SCAN -type f -name '*.sh' 2>/dev/null | sort > "$TMP"
|
||||
[ -s "$TMP" ] || { echo "tools/ 與 hooks/ 底下沒有 *.sh,無腳本可掃:$DOMAIN" >&2; exit 3; }
|
||||
|
||||
has_exit_stmt() { # $1=檔案;去掉整行註解後還找得到 exit 敘述就回 0
|
||||
grep -v '^[[:space:]]*#' "$1" 2>/dev/null \
|
||||
| grep -qE '(^|[;&|(){}[:space:]])exit([[:space:]]|$)'
|
||||
}
|
||||
|
||||
is_library() { # $1=檔案;只被 source 的共用函式庫,可執行與結束碼兩項都豁免
|
||||
# 判準兩條,缺一不可:
|
||||
# 1. 自我宣告是函式庫——檔名 lib.sh,或檔頭前 30 行寫了「以 source 載入」。
|
||||
# 2. 程式碼裡一個 exit 敘述都沒有。沒有 exit 就沒有結束碼語意,也就沒有東西可宣告;
|
||||
# 這一條同時是「不是可執行入口」的證據,兩項豁免共用同一個事實。
|
||||
# 為什麼不能只看第 1 條: 那是字樣比對,會命中「提到這件事」的腳本。本腳本自己的
|
||||
# 例外段就寫了「以 source 載入」,plugins-root.sh 的用法段也寫了,但兩支都是有
|
||||
# 結束碼語意的可執行入口。只拿第 1 條去豁免結束碼,等於把工具自己漏掉。
|
||||
case "${1##*/}" in
|
||||
lib.sh) ;;
|
||||
*) head -30 "$1" 2>/dev/null | grep -q '以 source 載入' || return 1 ;;
|
||||
esac
|
||||
! has_exit_stmt "$1"
|
||||
}
|
||||
|
||||
hit=0
|
||||
total=0
|
||||
libs=0
|
||||
while IFS= read -r f; do
|
||||
[ -n "$f" ] || continue
|
||||
total=$((total + 1))
|
||||
|
||||
err=$(sh -n "$f" 2>&1) || {
|
||||
printf '%s:語法:%s\n' "$f" "$(printf '%s' "$err" | tr '\n' ' ')"
|
||||
hit=1
|
||||
}
|
||||
|
||||
if is_library "$f"; then
|
||||
libs=$((libs + 1))
|
||||
printf '%s:函式庫:判定為只被 source 的共用函式庫(無 exit 敘述),略過「可執行」與「結束碼」\n' "$f" >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ ! -x "$f" ]; then
|
||||
printf '%s:可執行:缺執行權限,請 chmod +x\n' "$f"
|
||||
hit=1
|
||||
fi
|
||||
|
||||
if ! head -60 "$f" 2>/dev/null | grep -qE '結束碼|exit code'; then
|
||||
printf '%s:結束碼:檔頭沒有結束碼宣告列,呼叫端無法逐碼分流\n' "$f"
|
||||
hit=1
|
||||
fi
|
||||
done < "$TMP"
|
||||
|
||||
if [ "$hit" -eq 0 ]; then
|
||||
echo "腳本檢查通過:$total 支(語法、可執行、結束碼宣告),其中 $libs 支判定為函式庫" >&2
|
||||
else
|
||||
echo "腳本檢查有不合格項目:共掃 $total 支(其中 $libs 支判定為函式庫),清單見 stdout" >&2
|
||||
fi
|
||||
exit $hit
|
||||
Reference in New Issue
Block a user