release: develop 併入 master(doctor、setup) #14

Merged
admin merged 3 commits from develop into master 2026-08-26 02:59:50 +00:00
6 changed files with 240 additions and 3 deletions
Showing only changes of commit e655f9a963 - Show all commits
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jsc-cli",
"version": "0.1.0",
"version": "0.1.2",
"description": "CLI 偵測、模型能力標籤、子代理派工與技能庫批次部署",
"skills": "./skills",
"author": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jsc-cli",
"version": "0.1.0",
"version": "0.1.2",
"description": "CLI 偵測、模型能力標籤、子代理派工與技能庫批次部署",
"skills": "./skills"
}
+13
View File
@@ -26,6 +26,9 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
| `tools/deploy.sh` | 對單一 CLI 執行安裝、更新或解除安裝(`deploy.sh [-n] {mode} {cli} {domain}...`,mode 為 install / update / uninstall);印出每個指令與其結束碼,最後一行 `result` 標 ok 或 fail。`-n` 只印指令不執行。上表五個 CLI 的指令差異全部收在這支腳本裡。站台取自 `GITEA_HOST`,本地 clone 目錄取自 `JSC_LOCAL_PLUGINS`,兩者的預設值見下表 |
| `tools/list-models.sh` | 讀各 CLI 設定檔列出模型(TSV:cli / model / in-use);設定檔缺失就不輸出該 CLI 的列,一律 exit 0。設定檔位置只寫在這支腳本裡 |
| `tools/model-config.sh` | 解析 SDLC 各階段的偏好模型鏈(`get {stage}`、`list`、`resolve {stage}` 印出目前 CLI 可用的第一個模型);專案 `.jsc/models` 優先於 `$JSC_HOME/models.conf`,格式見 `references/model-tags.md`。鏈只影響建議與偏好順序,不影響閘門放行 |
| `tools/config-spec.tsv` | 設定規格表:每個環境變數與設定檔一列,標明必要或選擇、預設值、驗證方式、修法。體檢與設定共用這一份,新增設定時要同步補一列 |
| `tools/scan-config.sh` | 依規格表盤點設定現況(`scan {global\|project\|all}` 印 TSV 與 summary、`spec` 印規格表、`orphans` 找出漏登錄的變數);`-o` 為離線模式,需要連 Gitea 的檢查一律標 skipped。唯讀,不寫任何設定;帶 TOKEN 的項目只印 set 或 unset |
| `tools/apply-config.sh` | 把設定寫進 shell rc 檔(`set {KEY} {VALUE}`、`unset {KEY}`)或建立目錄(`mkdir {PATH}`);`show` 印出目前設定,`rcfiles` 印出會寫入的檔案。內容一律收在 `# jsc-config` 標記段落之間,整段重寫不疊加,段落外不動。動檔案前先備份到 `$JSC_HOME/backup/config/{yyyyMMdd_HHmmss}/`,備份失敗就不寫;寫完重讀驗證。fish 自動改用 `set -gx` 語法 |
| `tools/model-tags.sh` | 解析 `references/model-tags.md` 的能力標籤與 SDLC 階段必要標籤(`dump`、`sync`、`stage {階段}`、`model {模型 id}`、`gate {階段} {模型 id}`);`sync` 寫出 `$JSC_HOME/model-tags.tsv` 供 `jsc-hooks` 的 sdlc-gate 讀取 |
## Skills 目錄
@@ -46,6 +49,14 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
技能庫批次安裝、更新、解除安裝:偵測 CLI → **先比對各 plugin 的本機與已發佈版本並列表,只要有任一個落後就把「更新」設為推薦選項** → 決策樹選模式 → 每個 CLI 一個 sub agent 呼叫 `tools/deploy.sh` 執行原生 plugin 指令(統一 marketplace `jsc`,token `jsc-{domain}@jsc`)。domain 名單動態取自 `plugins/meta` 的 marketplace.json,不硬編碼。
### `doctor`
一次體檢執行環境,只讀不改。四項檢查:技能版本(`jsc-hooks/hooks/version-guard.sh report`)、Hook 接線(`jsc-hooks/tools/wire-cli.sh status`,唯讀子命令)、全域設定與自我設定(`tools/scan-config.sh` 比對 `tools/config-spec.tsv`)。每項各出一張表,整份結果寫進 wiki `CHECK_{HASH}`,`HASH` 取 `{主機名}/{登入帳號}`,只保留最新一次。修復交給 `/jsc-cli:setup`,體檢本身不動任何設定。
### `setup`
修復 `/jsc-cli:doctor` 找出的問題,一次一項,逐項確認才動手。待修清單優先讀 wiki `CHECK_{HASH}`,沒有頁面就當場重掃一份。依修法分流:`auto` 用 `tools/apply-config.sh` 直接寫、`ask` 先用決策樹問到值再寫、`manual` 印出步驟交給操作者。複合修復交回原主:版本落後找 `/jsc-cli:deploy`、hook 未接線找 `/jsc-hooks:hooks-install`、缺 `model-tags.tsv` 找 `/jsc-cli:models`。每一項寫完都重驗一次,最後覆寫 CHECK 頁。
<!-- JSC-SKILLS:END -->
## 環境變數
@@ -57,6 +68,8 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
| `JSC_LOCAL_PLUGINS` | antigravity 與 kiro 退路用的本地 clone 目錄 | 用 `$JSC_HOME/plugins`(即 `~/.jsc/plugins`) |
| `JSC_KIRO_SKILLS` | kiro 退路複製 skills 的目標目錄 | 用 `~/.kiro/skills` |
| `JSC_DEPLOY_DRYRUN` | 設為 `1` 等同 `deploy.sh -n`,只印指令不執行 | 照常執行 |
| `JSC_WIKI_REPO_CHECK` | 體檢頁 `CHECK_CONTENTS`、`CHECK_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO`;兩個都沒有就略過寫入,並把這一項列進待修 |
| `JSC_CONFIG_SPEC` | 改讀別份設定規格表(測試 `scan-config.sh` 時用) | 用 `tools/config-spec.tsv` |
`JSC_LOCAL_PLUGINS` 的預設值刻意避開 `~/plugins`:那是維護者放技能組開發 checkout 的地方,`git pull` 下去會蓋掉未提交的工作。這個變數指到的目錄若是開發中的樹(有未提交變更,或有未推送的 commit),`deploy.sh` 只印一行 `skip` 並直接用現地內容安裝,不執行 `git pull`。
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jsc-cli",
"version": "0.1.0",
"version": "0.1.2",
"description": "CLI 偵測、模型能力標籤、子代理派工與技能庫批次部署",
"skills": "./skills/"
}
+66
View File
@@ -0,0 +1,66 @@
---
name: setup
description: Fix what jsc-cli:doctor found, one confirmed item at a time. Read the 待修項目 table from wiki CHECK_{HASH}, or rebuild it with tools/scan-config.sh and jsc-hooks/tools/wire-cli.sh status when no page exists. Route each item by its fix column - auto writes it through tools/apply-config.sh, ask collects the value through the jsc-ask decision tree first, manual prints the steps for the operator. Delegate compound repairs to their owners: jsc-cli:deploy for a plugin whose version is behind, jsc-hooks:hooks-install for unwired hooks, jsc-cli:models for a missing model-tags.tsv. Re-verify every item after writing and rewrite the CHECK page; use when doctor reports something to fix, not for a read-only checkup.
---
# setup — guide or apply the fixes doctor found
This skill writes. Every write is confirmed first, backed up, and verified afterwards.
## 1. Get the work list
Read the 待修項目 table from wiki `CHECK_{HASH}` — repo from `jsc-gitea/tools/gitea.sh wiki-repo CHECK`, page name from `gitea.sh hash-id "{hostname}/{user}"`.
No page, or `wiki-repo` exits 3 → rebuild the list here: `tools/scan-config.sh scan all` for settings, `jsc-hooks/tools/wire-cli.sh status {cli}` per detected CLI for wiring, `jsc-hooks/hooks/version-guard.sh report` for versions. Rebuilding **MUST run as a sub agent**.
State which source the list came from. A stale page and a live scan can disagree, and the operator has to know which one is on screen.
Done when every item carries its scope, verdict and fix route.
## 2. Confirm each item
Ask per the `jsc-ask:ask` decision tree, one item at a time, in the table's order. Every option states its impact scope: which file gets written, which skills start working, what stays broken when skipped.
An `ask` item needs its value in the same question — the wiki repo as `{owner}/{repo}`, the Gitea host, the directory path. Never invent one.
Skipping is always an option and is recorded as skipped, not as fixed.
Done when every item is either confirmed with a value or recorded as skipped.
## 3. Apply
| Route | Action |
| --- | --- |
| `auto` on a variable | `tools/apply-config.sh set {KEY} {VALUE}` |
| `auto` on a directory | `tools/apply-config.sh mkdir {PATH}` |
| `ask` | same two commands, with the value the user just gave |
| `manual` | print the exact steps and the file to edit; the operator does it |
| domain 落後 | call `jsc-cli:deploy`, mode `update` |
| hook unwired | call `jsc-hooks:hooks-install` |
| `$JSC_HOME/model-tags.tsv` missing | call `jsc-cli:models` |
`apply-config.sh` writes into the `# jsc-config` block of every existing shell rc file, backs each one up to `$JSC_HOME/backup/config/{timestamp}/` before touching it, and rewrites the block whole. It never edits anything outside that block.
Report the `backup` path it prints. That path is the whole undo story for this run.
Done when every confirmed item has a `wrote`, `created` or delegated result.
## 4. Re-verify
Rerun the check that produced each item — `tools/scan-config.sh scan {scope}` for settings, `wire-cli.sh status {cli}` for wiring, `version-guard.sh report` for versions.
An item that still fails is reported as 未修好 with the reason. Never mark it fixed because the write succeeded: writing the variable and the variable verifying are two different facts.
A newly written rc block does not affect the running shell. Tell the operator to open a new shell or `source` the rc file, and give them the `export` line for the current session. A re-verify that reads the current environment will still show the variable unset — say so rather than reporting a false failure.
Done when every applied item has a fresh verdict from its own checker.
## 5. Record
Rewrite `CHECK_{HASH}` through `jsc-gitea:wiki` with the post-fix state, per `templates/check-page.md`, and refresh the `CHECK_CONTENTS` row. The page keeps only the latest run, so this overwrites the pre-fix picture on purpose.
No wiki repo configured → report the tables on screen and say the record was skipped.
Then state the counts: fixed, skipped, delegated, and 未修好. Recommend `/jsc-cli:doctor` for a clean re-check when anything was delegated.
Done when the page is written or the skip is reported, and the four counts are stated.
+158
View File
@@ -0,0 +1,158 @@
#!/usr/bin/env sh
# apply-config.sh — 把設定寫進 shell rc 檔或建立設定目錄(供 /jsc-cli:setup 呼叫)。
# 用法:
# apply-config.sh set {KEY} {VALUE} # 寫入或更新一個環境變數(export)
# apply-config.sh unset {KEY} # 從 jsc 段落移除一個環境變數
# apply-config.sh mkdir {PATH} # 建立目錄(值可帶 $VAR 與開頭的 ~)
# apply-config.sh show # 印出目前 jsc 段落的內容(每行 KEY<TAB>VALUE)
# apply-config.sh rcfiles # 印出這次會寫入的 rc 檔路徑
#
# 寫入位置:每個既有的 shell rc 檔(~/.bashrc、~/.zshrc、~/.config/fish/config.fish)
# 都寫一份,全部不存在時才建立 ~/.bashrc。內容一律收在標記段落之間:
# # jsc-config
# export KEY='值'
# # /jsc-config
# 段落整段重寫,重跑只取代不疊加。段落外的內容一律不動——rc 檔是使用者自己的檔案,
# jsc 只負責自己那一段。
#
# 動到任何檔案之前先原樣備份到 $JSC_HOME/backup/config/{yyyyMMdd_HHmmss}/,備份失敗就不寫入。
# fish 的語法與 POSIX shell 不同,寫進去的是 set -gx KEY 值。
#
# 結束碼: 0=成功 2=用法錯誤 4=備份或寫入失敗
set -eu
JSC_HOME="${JSC_HOME:-$HOME/.jsc}"
MARK_OPEN='# jsc-config'
MARK_SHUT='# /jsc-config'
TAB=$(printf '\t')
usage() {
echo "用法:apply-config.sh {set {KEY} {VALUE}|unset {KEY}|mkdir {PATH}|show|rcfiles}" >&2
exit 2
}
cmd="${1:-}"
case "$cmd" in set|unset|mkdir|show|rcfiles) ;; *) usage ;; esac
rc_files() {
found=""
for f in "$HOME/.bashrc" "$HOME/.zshrc" "$HOME/.config/fish/config.fish"; do
[ -f "$f" ] && { printf '%s\n' "$f"; found=1; }
done
[ -n "$found" ] || printf '%s\n' "$HOME/.bashrc"
}
if [ "$cmd" = rcfiles ]; then rc_files; exit 0; fi
# 目前段落裡的設定,格式 KEY<TAB>VALUE。取第一個既有 rc 檔為準:寫入時每個檔案內容相同。
read_pairs() {
for f in $(rc_files); do
[ -f "$f" ] || continue
awk -v o="$MARK_OPEN" -v s="$MARK_SHUT" '
$0==o { inb=1; next }
$0==s { inb=0; next }
inb {
line=$0
sub(/^export /, "", line) # POSIX shell
sub(/^set -gx /, "", line) # fish
if (line ~ /^[A-Za-z_][A-Za-z0-9_]*=/) {
eq=index(line, "="); k=substr(line, 1, eq-1); v=substr(line, eq+1)
} else {
sp=index(line, " "); if (sp==0) next
k=substr(line, 1, sp-1); v=substr(line, sp+1)
}
gsub(/^'"'"'|'"'"'$/, "", v)
if (k != "") print k "\t" v
}
' "$f"
return 0
done
}
if [ "$cmd" = show ]; then read_pairs; exit 0; fi
if [ "$cmd" = mkdir ]; then
raw="${2:-}"; [ -n "$raw" ] || usage
case "$raw" in "~/"*) raw="$HOME/${raw#\~/}" ;; esac
path=$( set +u; eval "printf '%s' \"$raw\"" )
[ -n "$path" ] || { echo "路徑展開後是空的:${2:-}" >&2; exit 4; }
mkdir -p "$path" 2>/dev/null || { echo "無法建立目錄:$path" >&2; exit 4; }
printf 'created\t%s\n' "$path"
exit 0
fi
key="${2:-}"
[ -n "$key" ] || usage
case "$key" in
[A-Za-z_]*) ;;
*) echo "變數名不合法:$key" >&2; exit 2 ;;
esac
value="${3:-}"
if [ "$cmd" = set ] && [ -z "$value" ]; then echo "set 需要值" >&2; exit 2; fi
# 合併:既有設定 + 這次的異動,重寫整段。少了這一步,寫第二個變數會蓋掉第一個。
pairs=$(mktemp) || { echo "無法建立暫存檔" >&2; exit 4; }
read_pairs > "$pairs" 2>/dev/null || true
merged=$(mktemp) || { rm -f "$pairs"; echo "無法建立暫存檔" >&2; exit 4; }
while IFS="$TAB" read -r k v; do
[ -n "${k:-}" ] || continue
[ "$k" = "$key" ] && continue
printf '%s\t%s\n' "$k" "$v" >> "$merged"
done < "$pairs"
rm -f "$pairs"
if [ "$cmd" = set ]; then printf '%s\t%s\n' "$key" "$value" >> "$merged"; fi
# 備份:動到的每個檔案先原樣複製一份,備份不了就整個不寫。
stamp=$(date +%Y%m%d_%H%M%S)
backup_dir="$JSC_HOME/backup/config/$stamp"
mkdir -p "$backup_dir" 2>/dev/null || { rm -f "$merged"; echo "無法建立備份目錄:$backup_dir" >&2; exit 4; }
# 依 rc 檔語法組出段落內容。fish 用 set -gx,其餘用 export。
block_for() {
_f="$1"
while IFS="$TAB" read -r k v; do
[ -n "${k:-}" ] || continue
case "$_f" in
*config.fish) printf "set -gx %s '%s'\n" "$k" "$v" ;;
*) printf "export %s='%s'\n" "$k" "$v" ;;
esac
done < "$merged"
}
rc_list=$(mktemp) || { rm -f "$merged"; echo "無法建立暫存檔" >&2; exit 4; }
rc_files > "$rc_list"
rc=0
while IFS= read -r f; do
[ -n "$f" ] || continue
if [ -f "$f" ]; then
cp -p "$f" "$backup_dir/$(basename "$f")" 2>/dev/null \
|| { echo "無法備份 $f,沒有備份就不寫入" >&2; rc=4; continue; }
else
mkdir -p "$(dirname "$f")" 2>/dev/null || { echo "無法建立 $(dirname "$f")" >&2; rc=4; continue; }
touch "$f" 2>/dev/null || { echo "無法建立 $f" >&2; rc=4; continue; }
fi
content=$(block_for "$f")
block=$(printf '%s\n%s\n%s' "$MARK_OPEN" "$content" "$MARK_SHUT")
if grep -qF "$MARK_OPEN" "$f" 2>/dev/null; then
awk -v o="$MARK_OPEN" -v s="$MARK_SHUT" -v b="$block" '
$0==o { print b; skip=1; next }
$0==s { skip=0; next }
skip { next }
{ print }
' "$f" > "$f.jsc-tmp" 2>/dev/null || { rm -f "$f.jsc-tmp"; echo "無法改寫 $f" >&2; rc=4; continue; }
mv "$f.jsc-tmp" "$f" 2>/dev/null || { rm -f "$f.jsc-tmp"; echo "無法覆寫 $f" >&2; rc=4; continue; }
else
( printf '\n%s\n' "$block" >> "$f" ) 2>/dev/null || { echo "無法寫入 $f" >&2; rc=4; continue; }
fi
# 寫完重讀驗證:說寫好了卻沒寫進去,是最難查的失敗
if [ "$cmd" = set ]; then
grep -qF "$key" "$f" 2>/dev/null || { echo "$f 寫入後讀不到 $key" >&2; rc=4; continue; }
fi
printf 'wrote\t%s\n' "$f"
done < "$rc_list"
rm -f "$rc_list" "$merged"
printf 'backup\t%s\n' "$backup_dir"
[ "$rc" = 0 ] || exit 4
printf 'note\t%s\n' "新的設定要開新的 shell 或重新 source rc 檔才生效;本輪工作階段可先手動 export"
exit 0