Files
cli/skills/deploy/SKILL.md
T
jiantw83 e30bbf5780 feat(cli): 待修項目合併收成共用腳本,檢查改為併行
「待修項目」表原本由 doctor 與 setup 各寫一次。同一套合併與排序規則寫在
兩個地方,遲早各自漂移:一邊改了排序,另一邊漏掉一整類項目,而且沒有
任何地方看得出來。現在規則只留一份,兩支技能都呼叫它。輸入與輸出都是
TSV,一項都沒有時照樣印一列,呼叫端永遠有東西可以呈現。

doctor 的四項檢查彼此不共用資料,排成一列跑只是把等待時間乘上四倍,
現在同時啟動。doctor 呼叫接線腳本一律帶唯讀旗標,把「打錯一個子命令就
改到或刪掉檔案」的風險移進程式層,不再只靠指令打對。

deploy 的 CLI 偵測、版本結論與 marketplace 清單同樣互不相干,改成併行
取得。版本結論改讀版本守門腳本的單行結論,不再自己從表格推導。setup 把
已經確認過的模式與版本報告直接交給 deploy,操作者不必再答一次同樣的問題。

deploy、doctor、models 都補上結束碼分流:腳本回什麼碼就走哪條路,不再從
輸出內容猜。體檢目錄頁改成先讀回再更新自己那一列,整頁覆蓋會把別台機器
的紀錄一次抹掉。設定規格表補上技能盤點頁要用的環境變數,盤點頁才有地方
可寫。
2026-08-31 11:09:59 +08:00

11 KiB
Raw Blame History

name, description
name description
deploy Batch install, update, or uninstall the whole jsc skill set on every installed AI CLI. Detect CLIs, read the version recommendation and the marketplace domain list in parallel, then ask the user for the mode via decision tree unless the caller already passed one, then run each CLI's native plugin commands in parallel with the unified jsc marketplace (token jsc-{domain}@jsc). Domain list comes from the plugins/meta marketplace.json, never hardcoded. After install or update, hand the detected CLI list to jsc-hooks:hooks-install, write this machine's update and remove guides via write-guides.sh, and demand a session restart. Use for rollout or removal of the jsc plugins; not for a single skill.

deploy — batch install, update, or uninstall the skill set

Inputs a caller may pass

jsc-cli:setup already confirmed the mode with the user and already holds a fresh version report. Re-asking and re-querying would put a second decision tree in front of someone who just answered it.

Input Effect
mode (install / update / uninstall) Step 3 skips the question and states which caller set the mode
version report Step 1 skips its version collector; step 2 shows the report it was handed and names its source

Nothing passed in → run every step as written below.

Steps

  1. Collect the three facts the rest of the run needs. They are independent, so start all three at once and wait for all three.

    1. Installed CLIs — tools/detect-clis.sh, printing {name}<TAB>{path}<TAB>{version}. Exit 0 with at least one row → take the CLI list from it. Exit 0 with no row → stop, and report that none of claude, codex, copilot, antigravity, kiro is installed. Any non-zero exit → stop and report the exit code and stderr; never guess a CLI list.
    2. Version evidence and recommendation — two subcommands of jsc-hooks/hooks/version-guard.sh, both needed, run together: report prints the per-plugin rows {domain}<TAB>{本機}<TAB>{遠端}<TAB>{落後|最新|超前|查詢失敗} closing with behind<TAB>{count}, and recommend prints one single line and nothing else — recommend<TAB>{update|none|unverifiable}. recommend deliberately never reprints the table, so its second column stays readable by cut; the version table that steps 2, 3 and 7 show comes from report, and the conclusion comes from recommend. Skip this collector when the caller passed a version report.
    3. Domain list — read plugins[].name from the unified marketplace (never hardcode it; this skill then follows automatically when domains are added or removed): jsc-gitea/tools/gitea.sh api GET /repos/plugins/meta/raw/.claude-plugin/marketplace.json. Exit 0 with at least one plugins[].name → use that list. Exit 0 with an empty or unparseable list → stop and report that the marketplace holds no plugin entry. Any non-zero exit → stop and report the exit code and stderr; a partial domain list would install a partial skill set and look successful.

    The marketplace is unified as jsc; the install token is jsc-{domain}@jsc. Each plugins[].name already carries the jsc- prefix (e.g. jsc-ask) — pass it to tools/deploy.sh as-is, prefixed or not; the script normalizes it.

    Done when the CLI list holds at least one CLI, the domain list holds at least one name, and the recommendation is either in hand or explicitly inherited from the caller.

  2. Show the report version table to the user as-is. It is the evidence behind the recommendation, so never summarise it away. A report handed in by a caller is shown the same way, with a line naming that caller as its source.

    Branch on the recommend line:

    Exit recommend value What it means and what to say
    0 update At least one domain is behind. Mark update as the recommended option in step 3, and name every behind domain with its local and remote version. One domain behind is enough; do not wait for a majority
    0 none Every domain row is 最新 or 超前. Recommend nothing; present the three options neutrally
    0 unverifiable The version check could not run — no local plugin registry, or every remote lookup failed. Say so plainly and base no recommendation on it. Unverified is not the same as up to date
    0 no recommend line in the output at all This jsc-hooks build has no recommend subcommand — an older version-guard.sh treats the argument as a hook invocation and exits 0 without printing anything. Derive the same three values yourself from the report table already collected in step 1.2: any 落後 row → update; no {domain} row at all, or a noregistry line → unverifiable; otherwise none. Say in step 7's report that the recommendation came from this fallback path, not from recommend
    non-zero — Report the version check as unverifiable with the exit code and stderr, and carry on to step 3 without a recommendation

    Any single domain row reading 查詢失敗 is called out by name even when the overall value is none. An unverified domain is not the same as an up-to-date one, and must not be counted as either.

    Done when the table is on screen and the recommendation is stated as exactly one of update, none or unverifiable.

  3. Ask the user for the mode per the jsc-ask:ask rules: install / update / uninstall. Every option states its impact scope: which CLIs it touches and which configs it writes. A caller that already passed a mode skips this step, and the report names that caller instead.

    Done when the user has named exactly one of install, update or uninstall, or the inherited mode is named with its source.

  4. Run tools/deploy.sh {mode} {cli} {domain}... once per detected CLI, passing the whole domain list in one call so the marketplace command runs only once. This step MUST run as a sub agent, one sub agent per CLI, and all of them start together — the CLIs write to separate plugin directories, so serialising them only adds up their install times.

    The script prints cmd and exit lines for every command, one requires line before each domain update, optional compat lines for Codex cache links, and one result line at the end; -n prints the commands without running them.

    Exit Action
    0 Every command for that CLI succeeded. Record its result line
    1 At least one command failed. Record that CLI as failed and quote every exit line whose code is non-zero
    2 Usage error — the mode, the CLI name or the domain list is wrong. Report it as a defect in this skill, and do not retry with a guessed argument
    other Record that CLI as failed with the exit code and stderr

    On update, tools/check-requires.sh {cli} {manifest} checks each domain's jsc.requires before that domain is updated. A missing or too-old required jsc plugin prints a skip line and leaves that domain untouched. Codex update preserves old jsc-cli and jsc-hooks cache version paths as symlinks to the newest installed version, so a still-running Codex deploy can keep using its helper scripts and a still-running Codex session whose hook_run_id points at the old cache can finish without No such file. Antigravity cannot install from a Gitea URL, so the script clones each domain into the local plugin directory (JSC_LOCAL_PLUGINS, default $JSC_HOME/plugins) and installs from that path — keep that clone, because update pulls the same one. That default deliberately avoids a development checkout: when the directory holds uncommitted changes or unpushed commits, the script prints a skip line, leaves the tree untouched, and installs the on-disk content.

    Done when every detected CLI has reported an exit code and a result line, and every skipped domain has a dependency reason or a local-tree reason.

  5. After install or update, call jsc-hooks:hooks-install and hand it the CLI list from step 1.1, so it does not probe the same five executables a second time. hooks-install still detects for itself when it receives no list — that fallback is what keeps it usable on its own.

    Take its aggregate result rather than re-reading each CLI's smoke detail; the installer already judged purge, wiring, smoke and scan per CLI, and refreshes $JSC_HOME/current/jsc-hooks on the way — the path deploy.sh follows to reach restart-gate.sh. Keep exactly one extra judgement here, because it is a deploy-side fact the installer does not rule on: a smoke result containing No such file is a failed update, since it means a rewritten hook path cannot execute. Report it and do not let the deploy finish as successful.

    Done when hooks-install has returned an aggregate verdict for every CLI in the list, and every No such file in it is reported as an update failure.

  6. After install or update, run tools/write-guides.sh {mode} {domain}... once for the whole machine, after every CLI in step 4 has finished. It rewrites $JSC_HOME/update-guide.md and $JSC_HOME/remove-guide.md from the live detection result, so the later update and removal runs have the real commands for this machine. Skip it for uninstall: the guides describe an installed skill set.

    Exit Action
    0 Both wrote lines printed. Name both paths in step 7
    2 Usage error — the mode or the domain list is wrong. Report it as a defect in this skill; the deploy itself still stands
    4 $JSC_HOME or one of the two files could not be written. Name the path and the stderr, and say the machine has no up-to-date guide until this is fixed
    other Report the guide write as failed with the exit code, and say which of the two files did print a wrote line

    Done when both wrote lines are printed, or the failure is reported with the exit code and the paths involved.

  7. Report the run and close it, in one block. The result and any failure reason for every CLI × mode, plus every skip line, every Codex compat line, and every CLI that could not be version-checked in step 2.

    For install or update, the same block ends with the restart instruction, in these words: 「請關閉目前的工作階段並重新啟動,新的技能內容才會載入」. deploy.sh recorded this round in $JSC_HOME/restart-required.d/{cli} — one file per CLI — and prints its path on a restart line; jsc-hooks reads only that CLI's own file and keeps reminding until that CLI restarts, with JSC_RESTART_GATE=off as the escape hatch. Restarting one CLI clears its own file and leaves the others' gates standing. Name the two guide paths from step 6 in that same closing block, so the operator knows where this machine's update and removal commands now live.

    Done when every detected CLI appears in the report with its result status, and — for install or update — the restart instruction is printed with both guide paths named, or step 6's failure is repeated in their place.