Files
cli/skills/setup/SKILL.md
T
jiantw83 fb80559159 feat(wiki): 體檢目錄頁改走專用存取庫,並補齊設定規格表
What:CHECK_CONTENTS 改由 wiki-repo CONTENTS 解析並透過 wiki-contents.sh upsert
寫入,CHECK_{HASH} 仍走 wiki-repo CHECK。目錄頁新增一欄裸 HASH 當比對鍵。主機名
改由程式取短名,不再交給模型自由填。

Why:比對鍵原本是含網址的儲存格,換主機或換存取庫就比對不到,每跑一次體檢就替同一台
機器多附一列,畫面上還看不出來。主機名短名與 FQDN 不一致時,同一台機器會分裂成兩張頁,
而助理巡檢那邊是用程式取值的,兩邊對不起來。

How:設定規格表同一輪補齊三處既有缺漏——補上漏掉的 JSC_WIKI_REPO_MONITOR,體檢本來
看不到它而孤兒掃描還會誤報;刪掉指向不存在頁面的 MAINTAIN 內容頁字樣;MAINTAIN 那一列
改成不需要使用者處理,免得體檢叫人去設一支管不到任何頁的變數。整列保留,刪掉會讓孤兒
掃描開始誤報那個變數。

Who:jsc-cli
2026-09-02 11:03:07 +08:00

12 KiB

name, description
name description
setup Fix what jsc-cli:doctor found, one confirmed item at a time. Read the 待修項目 table from wiki CHECK_{HASH}, or rebuild it by running the three checkers in parallel and merging them with tools/build-todo.sh. 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, handing jsc-cli:deploy the mode and the version report it already has. 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 "{host}/{user}", where host is the short hostname and user the login account, both taken from the machine:

host=$(hostname 2>/dev/null || uname -n 2>/dev/null || printf 'unknown'); host=${host%%.*}
user=${USER:-$(id -un 2>/dev/null || printf 'unknown')}

${host%%.*} matters: hostname prints the FQDN on some machines, and a hash built on the long name reads a page doctor never wrote. This is the same value doctor hashes, so it must be taken the same way.

No page, or wiki-repo exits 3, or any other non-zero exit from wiki-repo, hash-id or the wiki read → rebuild the list here. Rebuilding MUST run as a sub agent, and its three checkers start together: they read different files and share no state, so serialising them only triples the wait.

Checker Command Exit branching
Settings tools/scan-config.sh scan all 0 → use the rows; 2 → usage error, report it as a defect in this skill; 3 → spec table missing, name the path and JSC_CONFIG_SPEC; other → report settings as 無法驗證
Wiring tools/detect-clis.sh, then JSC_READONLY=1 jsc-hooks/tools/wire-cli.sh status {cli} per detected CLI — this step only takes stock, and wire-cli.sh without a subcommand rewires, so the read-only contract is carried in the environment rather than trusted to a correctly typed subcommand detect-clis exit 0 with no row → no CLI to wire, say so and skip; detect-clis non-zero → report wiring as 無法驗證 with the exit code. Per CLI: 0 wired and 1 degraded → nothing to fix; 2 → usage error, defect in this skill; 3 → CLI not installed, drop it; 5 → collect its missing items; 6 → readonly refused the call, which means the subcommand was mistyped into a writing one — nothing on the machine changed; fix the command and rerun that CLI; other → report that CLI as 無法驗證
Versions jsc-hooks/hooks/version-guard.sh report 0 → use the rows, and treat a report with no {domain} row or a noregistry line as 無法驗證 — other exits → report versions as 無法驗證 with the exit code

Merge the three with tools/build-todo.sh --config {設定輸出} --wiring {cli}={接線輸出} --version {版本輸出} so the ordering rule lives in one place. Exit 0 → the todo rows are the work list; exit 2 → usage error, report it as a defect in this skill; exit 3 → name the unreadable input and rerun that one checker; any other exit → stop and report, because a half-merged list would silently drop a whole class of items.

Keep the version report from that run. Step 3 hands it to jsc-cli:deploy instead of making it query again.

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 class, scope, current state and fix route, and the source of the list is named.

2. Confirm each item

Ask per the jsc-ask:ask decision tree, one item at a time, in the table's order. Confirmation stays strictly sequential: each answer can change what the next item should be, and a batch of questions fired at once takes that away from the operator.

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 with mode update and the version report from step 1, so it neither re-asks the mode nor re-queries the versions
hook unwired call jsc-hooks:hooks-install
$JSC_HOME/model-tags.tsv missing call jsc-cli:models

apply-config.sh exit codes:

Exit Action
0 Written. Record the wrote or created result and the backup path
2 Usage error — the subcommand, the key or the value is wrong. Record the item as 未修好 with that reason, and do not retry with a guessed argument
4 Backup or write failed, so nothing was written. Record the item as 未修好 and name the rc file and the stderr, then say the machine is unchanged
other Record the item as 未修好 with the exit code and stderr. Never mark it fixed on an unrecognised exit

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, delegated or 未修好 result.

4. Re-verify

Re-verify every applied item. The items are independent, so run the re-verifications in parallel — one batch, one wait. Only the confirmation in step 2 has to stay sequential.

Pick the check by what was actually written, because the two kinds of write become true at different moments:

What was written Re-verify with Why this check
An environment variable in a shell rc file tools/apply-config.sh show, confirming the KEY<TAB>VALUE line is in the # jsc-config block The block is a fact that is already true. The variable reaching the environment is not — a rc file does not touch the running shell
A directory tools/scan-config.sh scan {scope}, confirming the row is no longer missing or invalid The directory exists the moment it is created
Wiring, versions, model tags (delegated) The owner skill's own returned result The owner already ran its own verification

The same exit branching as step 1 applies to scan-config.sh and to apply-config.sh.

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.

For every environment variable written, print the matching export KEY=VALUE line for the current session and tell the operator to open a new shell or source the rc file. The environment-level proof is handed to the next /jsc-cli:doctor run, which starts in a fresh shell — asserting it here would read the shell that could not have picked the value up yet, and report a false failure every time.

Done when every applied item has a fresh verdict from the checker its own row names, and every environment variable carries its export line.

5. Record

The two pages live in two different wiki repos. Resolve each one on its own.

Rewrite CHECK_{HASH} through jsc-gitea:wiki with the post-fix state, per templates/check-page.md — repo from gitea.sh wiki-repo CHECK. That page is a content page and keeps only the latest run, so this overwrites the pre-fix picture on purpose.

CHECK_CONTENTS is a contents page, it lives in the contents repo (gitea.sh wiki-repo CONTENTS, never a fallback to JSC_WIKI_REPO_CHECK), and it gets the opposite treatment. Write the row with

jsc-gitea/tools/wiki-contents.sh upsert CHECK 4 "{HASH}" {row file} templates/check-contents.md

which reads the page back and refreshes this machine's row, or appends it when missing. Never overwrite the whole page, and never touch another machine's row.

The key is column 4, the bare HASH. 4 is the 1-based index of the HASH column in templates/check-contents.md, and the key is exactly what hash-id printed for {host}/{user} in step 1 — 40 uppercase hex characters, not shortened, not prefixed, not wrapped in a link. The script compares the whole cell, so the key and that cell must match character for character.

A cell holding a URL would make a moving key: it changes with GITEA_HOST, with a move of JSC_WIKI_REPO_CHECK to another repo, and with Gitea's encoding of the page name. The comparison then never matches, and every run appends a second row for the same machine instead of updating it.

Column 1 stays the human-facing link and is never the key. Build it as an absolute URL from gitea.sh wiki-url {CHECK repo} CHECK_{HASH}, fetched after CHECK_{HASH} is rewritten. [[CHECK_{HASH}]] resolves only inside its own wiki, and the two pages are no longer in the same one.

Exit Action
0 Report the updated or added result with the repo and page it named
1 Write failed and nothing landed. Report it with the stderr
2 Usage error. Report it as a defect in this skill; do not retry with guessed arguments. A templates/check-contents.md that is not on disk also lands here — then name the path the script looked for, confirm the plugin install is complete, and rerun
3 No contents repo configured. Skip this write and report JSC_WIKI_REPO_CONTENTS as still unfixed
4 Unreachable the way this skill calls the script — the command above always passes templates/check-contents.md, and a template that is not on disk comes back as exit 2. So treat a 4 as a malformed call: report it as a defect in this skill, name the command that produced it, and do not retry with guessed arguments
7 Key invalid or no permission. Nothing was read or written; name the exit code and create nothing
8 Any other API failure. Same as 7

Exits 7 and 8 never mean the page is missing: the whole-page overwrite that is correct for CHECK_{HASH} would here destroy every other machine's row, unread and unrecoverable. The script creates a page only when its own read reported that page absent, and it owns that branch.

gitea.sh wiki-url has its own exits, and they are read before the upsert runs. Exit 4 means CHECK_{HASH} is not on the wiki yet, so rewrite that page first and fetch the URL again. Any other non-zero exit: name the exit code and stop — never hand-build the URL, because a guessed link goes into the row and points nowhere.

No wiki repo configured, or any non-zero exit from wiki-repo, hash-id, wiki-url or the wiki write → report the tables on screen, say the record was skipped, and name the exit code.

Then state the counts: fixed, skipped, delegated, and 未修好. Recommend /jsc-cli:doctor for a clean re-check when anything was delegated.

Done when each of the two pages is written or its skip is reported, and the four counts are stated.