Files
meta/tools/inventory-tooling.sh
T
jiantw83 7dc5c32de3 fix(tools): 改用共用推導取得技能組根目錄,並補齊結束碼宣告
以 plugin 形式安裝時,腳本會落在 CLI 的 plugin 快取目錄。
六支腳本原本一律取「腳本位置的上兩層」當技能組根目錄,這時一定推錯。
這一輪例行稽核的第一步就實際踩到:不先手動設環境變數,腳本根本跑不動,
而叫用它們的技能說明也沒提要設,等於留了一個必炸的預設值。

把推導規則抽成共用腳本,依序試環境變數、從目前目錄往上找、
腳本位置的上兩層、家目錄底下的 plugins,並以 gitea.sh 在不在當判準。
marketplace 每個 domain 存取庫都帶一份,單看它會把 domain 誤判成根。
推不出來就結束並指名要設環境變數,同時列出試過的每一個候選,
呼叫端一眼看得出要設什麼。只 clone 單一存取庫的環境留了逃生門:
環境變數有設且是目錄就照用,並在錯誤輸出提醒。

順手補上語言檢查與 manifest 同步兩支腳本的結束碼宣告與環境變數說明。
沒有宣告,呼叫端只能猜;猜錯就把失敗當成功。
2026-08-31 11:11:12 +08:00

284 lines
8.4 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env sh
# inventory-tooling.sh — 盤點 jsc plugins、skills、tools、hooks,輸出技能組基礎指引。
#
# 用法: inventory-tooling.sh [root]
#
# root: 給了參數就用參數。沒給就交給 tools/plugins-root.sh 推導
# (JSC_PLUGINS_ROOT -> 從 $PWD 往上找 -> 腳本位置上兩層 -> $HOME/plugins)。
# 以 plugin 形式安裝時,「腳本位置上兩層」會落在 plugin 快取目錄,單靠它一定推錯,
# 所以推導規則抽成共用腳本。
#
# 輸出: Markdown。內容包含 domain、manifest、技能、工具、hooks 與管理入口。
# 同一趟已經跑過 list-skills.sh、detect-clis.sh 與 wire-cli.sh status,結果都寫進輸出,
# 呼叫端沿用即可,不必再各跑一次。
# 結束碼: 0=成功 1=推導不出根目錄、根目錄不存在、marketplace 或必要工具缺失
# 環境變數: JSC_PLUGINS_ROOT(根目錄,見 plugins-root.sh)
set -eu
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
. "$HERE/plugins-root.sh"
if [ "$#" -ge 1 ] && [ -n "$1" ]; then
ROOT=$1
else
ROOT=$(jsc_plugins_root) || exit 1
fi
ROOT=${ROOT%/}
[ -d "$ROOT" ] || { echo "找不到 plugins 根目錄:$ROOT" >&2; exit 1; }
MARKETPLACE=""
for cand in "$ROOT/meta/.claude-plugin/marketplace.json" "$ROOT"/*/.claude-plugin/marketplace.json; do
[ -f "$cand" ] && { MARKETPLACE=$cand; break; }
done
[ -n "$MARKETPLACE" ] || { echo "找不到 marketplace.json,無法判定 domain 清單" >&2; exit 1; }
LIST_SKILLS="$ROOT/meta/tools/list-skills.sh"
[ -x "$LIST_SKILLS" ] || { echo "找不到可執行的 list-skills.sh:$LIST_SKILLS" >&2; exit 1; }
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
domains_file="$tmp/domains.tsv"
skills_file="$tmp/skills.tsv"
clis_file="$tmp/clis.tsv"
hook_status_file="$tmp/hook-status.tsv"
sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"jsc-\([a-z0-9-]*\)".*/\1/p' "$MARKETPLACE" |
sort -u |
while read -r domain; do
[ -n "$domain" ] || continue
path="$ROOT/$domain"
[ -d "$path" ] || path="$ROOT/jsc-$domain"
if [ -d "$path" ]; then
version=$(sed -n 's/.*"version": *"\([^"]*\)".*/\1/p' "$path/plugin.json" 2>/dev/null | head -1)
[ -n "$version" ] || version="未知"
printf '%s\t%s\t%s\n' "$domain" "$version" "$path"
else
printf '%s\t%s\t%s\n' "$domain" "缺本機存取庫" "-"
fi
done > "$domains_file"
JSC_PLUGINS_ROOT="$ROOT" "$LIST_SKILLS" > "$skills_file" 2>/dev/null || : > "$skills_file"
DETECT_CLIS="$ROOT/cli/tools/detect-clis.sh"
if [ -x "$DETECT_CLIS" ]; then
"$DETECT_CLIS" > "$clis_file" 2>/dev/null || : > "$clis_file"
else
: > "$clis_file"
fi
WIRE_CLI="$ROOT/hooks/tools/wire-cli.sh"
: > "$hook_status_file"
if [ -x "$WIRE_CLI" ] && [ -s "$clis_file" ]; then
while IFS="$(printf '\t')" read -r cli path version; do
[ -n "$cli" ] || continue
out="$tmp/wire-$cli.out"
err="$tmp/wire-$cli.err"
if "$WIRE_CLI" status "$cli" > "$out" 2> "$err"; then
rc=0
else
rc=$?
fi
verdict=$(sed -n '1{s/[|]/-/g;p;}' "$out")
[ -n "$verdict" ] || verdict=$(sed -n '1{s/[|]/-/g;p;}' "$err")
[ -n "$verdict" ] || verdict="無狀態輸出"
case "$rc" in
0|1|3|5) ;;
2) verdict="CLI 代號不符合 wire-cli.sh 用法" ;;
*) verdict="未知狀態,結束碼 $rc" ;;
esac
printf '%s\t%s\t%s\n' "$cli" "$rc" "$verdict" >> "$hook_status_file"
done < "$clis_file"
fi
count_lines() {
file=$1
[ -s "$file" ] || { echo 0; return; }
wc -l < "$file" | tr -d ' '
}
domains_count=$(count_lines "$domains_file")
skills_count=$(count_lines "$skills_file")
clis_count=$(count_lines "$clis_file")
cat <<EOF
# jsc 技能組基礎指引
產生時間:$(date '+%Y-%m-%d %H:%M:%S %z')
資料來源:
- plugins 根目錄:\`$ROOT\`
- marketplace:\`$MARKETPLACE\`
- 技能清單工具:\`$LIST_SKILLS\`
## Source freshness
| 項目 | 狀態 |
| --- | --- |
| 本機 domain 清單 | synced |
| marketplace | \`$MARKETPLACE\` |
## 現況摘要
| 項目 | 數量 |
| --- | ---: |
| 已註冊 plugin domain | $domains_count |
| 已掃到技能 | $skills_count |
| 已偵測 CLI | $clis_count |
## Supported plugins
| domain | version | 本機路徑 |
| --- | --- | --- |
EOF
while IFS="$(printf '\t')" read -r domain version path; do
[ -n "$domain" ] || continue
printf '| `%s` | `%s` | `%s` |\n' "$domain" "$version" "$path"
done < "$domains_file"
cat <<'EOF'
## Supported skills
| domain | skill | 用途 |
| --- | --- | --- |
EOF
if [ -s "$skills_file" ]; then
while IFS="$(printf '\t')" read -r domain name desc; do
[ -n "$domain" ] || continue
printf '| `%s` | `%s` | %s |\n' "$domain" "$name" "$desc"
done < "$skills_file"
else
echo '| - | - | 未掃到技能。請先執行 `meta/tools/sync-domains.sh`。 |'
fi
cat <<'EOF'
Skill 使用方式:
- Claude、Antigravity:使用 `/jsc-{domain}:{name}`。
- Codex:使用技能名稱或自然語意觸發。
- Copilot、Kiro:用需求描述觸發。
- 每支技能只保留決策與流程。可標準輸入輸出的細節放到 `tools/`。
- 需要強制執行的規則放到 `jsc-hooks`,不要散落在各 domain。
## Supported CLIs
| CLI | 執行檔 | 版本 |
| --- | --- | --- |
EOF
if [ -s "$clis_file" ]; then
while IFS="$(printf '\t')" read -r cli path version; do
[ -n "$cli" ] || continue
printf '| `%s` | `%s` | `%s` |\n' "$cli" "$path" "$version"
done < "$clis_file"
else
echo '| - | - | 這台機器未偵測到支援的 CLI,或找不到 `cli/tools/detect-clis.sh`。 |'
fi
cat <<'EOF'
## Supported tools
| domain | tool | 用途 |
| --- | --- | --- |
EOF
while IFS="$(printf '\t')" read -r domain version path; do
[ -d "$path/tools" ] || continue
for tool in "$path"/tools/*; do
[ -f "$tool" ] || continue
base=$(basename "$tool")
first=$(sed -n '2{s/^# *//;p;}' "$tool" 2>/dev/null)
[ -n "$first" ] || first="工具腳本"
printf '| `%s` | `%s` | %s |\n' "$domain" "$base" "$first"
done
done < "$domains_file"
cat <<'EOF'
Tool 使用方式:
- 先讀工具檔頭的用法與結束碼。
- 用真實參數驗證新增或修改的工具。
- 結束碼沒有文件化時,先補工具說明,再讓技能呼叫它。
## Hook management
| hook | 用途 |
| --- | --- |
EOF
HOOK_DIR="$ROOT/hooks/hooks"
if [ -d "$HOOK_DIR" ]; then
for hook in "$HOOK_DIR"/*.sh; do
[ -f "$hook" ] || continue
base=$(basename "$hook")
first=$(sed -n '2{s/^# *//;p;}' "$hook" 2>/dev/null)
[ -n "$first" ] || first="hook 腳本"
printf '| `%s` | %s |\n' "$base" "$first"
done
else
echo '| - | 找不到 `hooks/hooks`。請先執行 `meta/tools/sync-domains.sh`。 |'
fi
cat <<'EOF'
Hook 使用方式:
- Hook 只放在 `jsc-hooks` domain。
- `jsc-hooks:hooks-install` 負責接線到各 CLI。
- hook 腳本要能接受 stdin JSON 與環境變數。
- 缺欄位時安靜降級並 `exit 0`。
- 版本閘門與重啟閘門要保留解除自己的路徑。
## Hook wiring status
| CLI | 結束碼 | 狀態 |
| --- | ---: | --- |
EOF
if [ -s "$hook_status_file" ]; then
while IFS="$(printf '\t')" read -r cli rc status; do
[ -n "$cli" ] || continue
printf '| `%s` | %s | %s |\n' "$cli" "$rc" "$status"
done < "$hook_status_file"
else
echo '| - | - | 未偵測到 CLI,或找不到 `hooks/tools/wire-cli.sh`。 |'
fi
cat <<'EOF'
## Plugin and skill management
- 新增技能:使用 `jsc-meta:skill-new`。
- 更新單一技能:使用 `jsc-meta:skill-update`。
- 批次更新技能組:使用 `jsc-meta:skillset-update`。
- 刪除技能:使用 `jsc-meta:skill-delete`。
- 安裝、更新、移除整組 plugin:使用 `jsc-cli:deploy`。
- 例行稽核:使用 `jsc-meta:skill-check`。
- 重新接線 hooks:使用 `jsc-hooks:hooks-install`。
## Operational checks
- 體檢目前環境:使用 `jsc-cli:doctor`。
- 修復體檢項目:使用 `jsc-cli:setup`。
- 版本前置檢查:由 `jsc-hooks/hooks/version-guard.sh` 管理。
- 部署後重啟閘門:由 `jsc-hooks/hooks/restart-gate.sh` 管理。
- 語言提示與掃描:由 `jsc-hooks/hooks/ste100-guard.sh` 與 `jsc-hooks/hooks/lang-guard.sh` 管理。
- 註解範圍檢查:由 `jsc-hooks/hooks/comment-scope.sh` 管理。
## Use this when
- 先執行 `meta/tools/sync-domains.sh`,同步 marketplace 上的 domain。
- 再執行 `meta/tools/list-skills.sh`,確認技能清單。
- 執行本工具,產出基礎指引。
- 若新增或修改技能,執行 `meta/tools/sync-skill-manifest.sh {domain-path}`。
- 執行 `meta/tools/ste100-lint.sh {domain-path}`。
- 依 `jsc-git:pr` 開立 Push Request。
EOF