feat(doctor): 加入 CLI 實測流程
This commit is contained in:
@@ -23,6 +23,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
|||||||
| 工具 | 用途 |
|
| 工具 | 用途 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `tools/detect-clis.sh` | 列出已安裝的 AI CLI 與執行檔路徑(TSV:name / path / version;antigravity 的執行檔為 `agy`、kiro 為 `kiro-cli`) |
|
| `tools/detect-clis.sh` | 列出已安裝的 AI CLI 與執行檔路徑(TSV:name / path / version;antigravity 的執行檔為 `agy`、kiro 為 `kiro-cli`) |
|
||||||
|
| `tools/test-clis.sh` | 實際呼叫已偵測到的 AI CLI,跑版本、說明頁與 plugin 清單等唯讀命令(`test-clis.sh [cli...]`);印出每項命令、結束碼、判定與輸出摘要,最後一行 `summary` 標出通過、降級、失敗、略過數。`JSC_CLI_TEST_TIMEOUT` 可調整單項命令逾時秒數,預設 10 秒 |
|
||||||
| `tools/deploy.sh` | 對單一 CLI 執行安裝、更新或解除安裝(`deploy.sh [-n] {mode} {cli} {domain}...`,mode 為 install / update / uninstall);印出每個指令與其結束碼,最後一行 `result` 標 ok 或 fail。`-n` 只印指令不執行。上表五個 CLI 的指令差異全部收在這支腳本裡。install 或 update 全數成功時,收尾轉呼叫 `jsc-hooks` 的 `restart-gate.sh require` 掛上重啟閘門,並印一行 `restart` 標出狀態檔位置;uninstall 不寫。尋找 `restart-gate.sh` 時優先用 `$JSC_HOME/current/jsc-hooks`、本地 clone 與 Kiro skills,最後才掃各 CLI 快取,避免部署收尾綁死單一 CLI 的版號路徑。狀態檔的路徑、格式與判讀全在 `restart-gate.sh`,這支腳本不自己拼——格式只留一個真實來源。站台取自 `GITEA_HOST`,本地 clone 目錄取自 `JSC_LOCAL_PLUGINS`,兩者的預設值見下表 |
|
| `tools/deploy.sh` | 對單一 CLI 執行安裝、更新或解除安裝(`deploy.sh [-n] {mode} {cli} {domain}...`,mode 為 install / update / uninstall);印出每個指令與其結束碼,最後一行 `result` 標 ok 或 fail。`-n` 只印指令不執行。上表五個 CLI 的指令差異全部收在這支腳本裡。install 或 update 全數成功時,收尾轉呼叫 `jsc-hooks` 的 `restart-gate.sh require` 掛上重啟閘門,並印一行 `restart` 標出狀態檔位置;uninstall 不寫。尋找 `restart-gate.sh` 時優先用 `$JSC_HOME/current/jsc-hooks`、本地 clone 與 Kiro skills,最後才掃各 CLI 快取,避免部署收尾綁死單一 CLI 的版號路徑。狀態檔的路徑、格式與判讀全在 `restart-gate.sh`,這支腳本不自己拼——格式只留一個真實來源。站台取自 `GITEA_HOST`,本地 clone 目錄取自 `JSC_LOCAL_PLUGINS`,兩者的預設值見下表 |
|
||||||
| `tools/check-requires.sh` | `check-requires.sh {cli} {manifest}` 檢查 manifest 的 `jsc.requires` 最低版本。沒有宣告就通過;版本不符或缺相依 plugin 就回 `status=blocked`。`deploy.sh update` 在每個 domain 更新前呼叫它,不符就跳過該 domain 並列出原因 |
|
| `tools/check-requires.sh` | `check-requires.sh {cli} {manifest}` 檢查 manifest 的 `jsc.requires` 最低版本。沒有宣告就通過;版本不符或缺相依 plugin 就回 `status=blocked`。`deploy.sh update` 在每個 domain 更新前呼叫它,不符就跳過該 domain 並列出原因 |
|
||||||
| `tools/write-guides.sh` | 產生這台機器專屬的更新指引 `$JSC_HOME/update-guide.md` 與移除指引 `$JSC_HOME/remove-guide.md`(`write-guides.sh [-n] {install\|update} {domain}...`),一輪部署跑一次。CLI 清單取自 `detect-clis.sh`,每支 CLI 的指令字面直接取自 `deploy.sh -n` 的輸出,所以指引寫的就是實際會跑的指令;kiro 走不走本地複製退路也依實際偵測結果標注 |
|
| `tools/write-guides.sh` | 產生這台機器專屬的更新指引 `$JSC_HOME/update-guide.md` 與移除指引 `$JSC_HOME/remove-guide.md`(`write-guides.sh [-n] {install\|update} {domain}...`),一輪部署跑一次。CLI 清單取自 `detect-clis.sh`,每支 CLI 的指令字面直接取自 `deploy.sh -n` 的輸出,所以指引寫的就是實際會跑的指令;kiro 走不走本地複製退路也依實際偵測結果標注 |
|
||||||
@@ -53,7 +54,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
|||||||
|
|
||||||
### `doctor`
|
### `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`,體檢本身不動任何設定。
|
一次體檢執行環境,只讀不改。五項檢查:技能版本(`jsc-hooks/hooks/version-guard.sh report`)、Hook 接線(`jsc-hooks/tools/wire-cli.sh status`,唯讀子命令)、CLI 實測(`tools/test-clis.sh` 實際呼叫版本、說明頁與 plugin 清單)、全域設定與自我設定(`tools/scan-config.sh` 比對 `tools/config-spec.tsv`)。每項各出一張表,整份結果寫進 wiki `CHECK_{HASH}`,`HASH` 取 `{主機名}/{登入帳號}`,只保留最新一次。修復交給 `/jsc-cli:setup`,體檢本身不動任何設定。
|
||||||
|
|
||||||
### `setup`
|
### `setup`
|
||||||
|
|
||||||
@@ -72,6 +73,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
|||||||
| `JSC_DEPLOY_DRYRUN` | 設為 `1` 等同 `deploy.sh -n`,只印指令不執行 | 照常執行 |
|
| `JSC_DEPLOY_DRYRUN` | 設為 `1` 等同 `deploy.sh -n`,只印指令不執行 | 照常執行 |
|
||||||
| `JSC_WIKI_REPO_CHECK` | 體檢頁 `CHECK_CONTENTS`、`CHECK_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO`;兩個都沒有就略過寫入,並把這一項列進待修 |
|
| `JSC_WIKI_REPO_CHECK` | 體檢頁 `CHECK_CONTENTS`、`CHECK_{HASH}` 所在的 `{owner}/{repo}` | 退回 `JSC_WIKI_REPO`;兩個都沒有就略過寫入,並把這一項列進待修 |
|
||||||
| `JSC_CONFIG_SPEC` | 改讀別份設定規格表(測試 `scan-config.sh` 時用) | 用 `tools/config-spec.tsv` |
|
| `JSC_CONFIG_SPEC` | 改讀別份設定規格表(測試 `scan-config.sh` 時用) | 用 `tools/config-spec.tsv` |
|
||||||
|
| `JSC_CLI_TEST_TIMEOUT` | `test-clis.sh` 單項 CLI 實測命令的逾時秒數 | 用 `10` |
|
||||||
| `JSC_HOME` | hook 資料目錄,兩份指引與重啟狀態檔都寫在這裡 | 用 `~/.jsc` |
|
| `JSC_HOME` | hook 資料目錄,兩份指引與重啟狀態檔都寫在這裡 | 用 `~/.jsc` |
|
||||||
| `JSC_RESTART_GATE` | 設成 `off` 可略過部署後的重啟提示閘門(判讀在 `jsc-hooks`,`jsc-cli` 只負責寫狀態檔) | 照常提示重啟 |
|
| `JSC_RESTART_GATE` | 設成 `off` 可略過部署後的重啟提示閘門(判讀在 `jsc-hooks`,`jsc-cli` 只負責寫狀態檔) | 照常提示重啟 |
|
||||||
|
|
||||||
|
|||||||
+39
-20
@@ -1,45 +1,64 @@
|
|||||||
---
|
---
|
||||||
name: doctor
|
name: doctor
|
||||||
description: Health-check the execution environment in one pass and record the result, changing nothing. Four checks - plugin versions from jsc-hooks/hooks/version-guard.sh report, hook wiring from jsc-hooks/tools/wire-cli.sh status, global settings and current-directory settings from tools/scan-config.sh against tools/config-spec.tsv. Report one findings table per check, then write the whole run to wiki CHECK_{HASH} where HASH comes from {hostname}/{user}; the page keeps only the latest run. Use after installing or updating the skill set, when a skill fails on a settings or wiring problem, or before handing a machine over; not for applying fixes, which is jsc-cli:setup.
|
description: Health-check the execution environment in one pass and record the result, changing nothing. Five checks - plugin versions from jsc-hooks/hooks/version-guard.sh report, hook wiring from jsc-hooks/tools/wire-cli.sh status, executable CLI tests from tools/test-clis.sh, global settings and current-directory settings from tools/scan-config.sh against tools/config-spec.tsv. Report one findings table per check, then write the whole run to wiki CHECK_{HASH} where HASH comes from {hostname}/{user}; the page keeps only the latest run. Use after installing or updating the skill set, when a skill fails on a settings, wiring or CLI runtime problem, or before handing a machine over; not for applying fixes, which is jsc-cli:setup.
|
||||||
---
|
---
|
||||||
|
|
||||||
# doctor — execution environment health check
|
# doctor - execution environment health check
|
||||||
|
|
||||||
Read-only. Every command below either reads a file or asks Gitea; none of them writes a setting. That is the contract with `jsc-cli:setup`: doctor states the facts, setup changes things.
|
Read-only. Every command below either reads local state, calls a read-only CLI command, or asks Gitea. None of them writes a setting. That is the contract with `jsc-cli:setup`: doctor states the facts, setup changes things.
|
||||||
|
|
||||||
Collection (steps 1 to 4) **MUST run as a sub agent** — one sub agent for all four, returning the raw TSV lines. Only the report and the wiki write stay in the main agent.
|
Collection (steps 1 to 5) **MUST run as a sub agent** - one sub agent for all five, returning the raw TSV lines. Only the report and the wiki write stay in the main agent.
|
||||||
|
|
||||||
## 1. Skill versions
|
## 1. Skill versions
|
||||||
|
|
||||||
Run `jsc-hooks/hooks/version-guard.sh report`. It prints `{domain}<TAB>{本機}<TAB>{遠端}<TAB>{落後|最新|超前|查詢失敗}` per plugin, then `behind<TAB>{count}`.
|
Run `jsc-hooks/hooks/version-guard.sh report`. It prints `{domain}<TAB>{local}<TAB>{remote}<TAB>{落後|最新|超前|查詢失敗}` per plugin, then `behind<TAB>{count}`.
|
||||||
|
|
||||||
A report with no `{domain}` row, or one carrying `noregistry<TAB>{path}`, means this CLI has no local plugin registry. Report it as 無法驗證 — never as 最新. `behind<TAB>0` proves nothing when no domain row precedes it.
|
A report with no `{domain}` row, or one carrying `noregistry<TAB>{path}`, means this CLI has no local plugin registry. Report it as `無法驗證` - never as `最新`. `behind<TAB>0` proves nothing when no domain row precedes it.
|
||||||
|
|
||||||
Done when every installed domain has a status literal, or the CLI is reported as unverifiable.
|
Done when every installed domain has a status literal, or the CLI is reported as unverifiable.
|
||||||
|
|
||||||
## 2. Hook wiring
|
## 2. Hook wiring
|
||||||
|
|
||||||
Run `jsc-hooks/tools/wire-cli.sh status {cli}` for every CLI that `tools/detect-clis.sh` found. Use `status` and nothing else: `wire-cli.sh` without a subcommand rewires, `purge` deletes, and `smoke` executes hooks — all three break the read-only contract.
|
Run `jsc-hooks/tools/wire-cli.sh status {cli}` for every CLI that `tools/detect-clis.sh` found. Use `status` and nothing else: `wire-cli.sh` without a subcommand rewires, `purge` deletes, and `smoke` executes hooks - all three break the read-only contract.
|
||||||
|
|
||||||
Exit codes: 0 wired, 1 degraded, 3 skipped (CLI not installed), 5 unwired. Each `item` line names one wiring point and whether it is present.
|
Exit codes: 0 wired, 1 degraded, 3 skipped because the CLI is not installed, 5 unwired. Each `item` line names one wiring point and whether it is present.
|
||||||
|
|
||||||
Only claude reaches `wired`. The other four have no pre-tool hook, so `degraded` is their healthy state — report the degradation reason as-is and never present it as a defect to fix.
|
Only claude reaches `wired`. The other four have no pre-tool hook, so `degraded` is their healthy state. Report the degradation reason as-is and never present it as a defect to fix.
|
||||||
|
|
||||||
Done when every detected CLI has a status and its missing items are listed.
|
Done when every detected CLI has a status and its missing items are listed.
|
||||||
|
|
||||||
## 3. Global settings
|
## 3. CLI runtime tests
|
||||||
|
|
||||||
|
Run `tools/test-clis.sh` with no CLI arguments. It calls `tools/detect-clis.sh`, then runs real read-only commands for every detected CLI.
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
- `test<TAB>{cli}<TAB>{test}<TAB>{command}<TAB>{exit-code}<TAB>{verdict}<TAB>{detail}`
|
||||||
|
- `summary<TAB>{ok}<TAB>{warn}<TAB>{fail}<TAB>{skipped}`
|
||||||
|
|
||||||
|
Verdicts: `ok`, `warn`, `fail`, `skipped`.
|
||||||
|
|
||||||
|
Exit codes: 0 completed, 2 usage error, 3 missing `detect-clis.sh`. Any other script exit code is itself a doctor finding.
|
||||||
|
|
||||||
|
Treat `fail` as a machine problem. Treat `warn` as degraded capability: name it in the report, but do not put it in the fix table unless the failing skill needs that feature. Treat `skipped` as no conclusion. Map the report labels to the template as `ok` -> `通過`, `warn` -> `降級`, `fail` -> `失敗`, and `skipped` -> `略過`.
|
||||||
|
|
||||||
|
Done when every detected CLI has at least a version test row and the summary line is read.
|
||||||
|
|
||||||
|
## 4. Global settings
|
||||||
|
|
||||||
Run `tools/scan-config.sh scan global`. It checks every `scope=global` row of `tools/config-spec.tsv` and prints `item<TAB>scope<TAB>required<TAB>actual<TAB>expect<TAB>fix<TAB>verdict`, closing with `summary<TAB>{missing}<TAB>{invalid}<TAB>{unset}<TAB>{skipped}`.
|
Run `tools/scan-config.sh scan global`. It checks every `scope=global` row of `tools/config-spec.tsv` and prints `item<TAB>scope<TAB>required<TAB>actual<TAB>expect<TAB>fix<TAB>verdict`, closing with `summary<TAB>{missing}<TAB>{invalid}<TAB>{unset}<TAB>{skipped}`.
|
||||||
|
|
||||||
Verdicts: `ok`, `default` (unset, default works), `unset` (optional, feature degrades), `missing` (required, skills break), `invalid` (set but fails verification), `skipped` (offline).
|
Verdicts: `ok`, `default`, `unset`, `missing`, `invalid`, `skipped`.
|
||||||
|
|
||||||
Add `-o` when Gitea is unreachable; the Gitea-dependent rows then come back `skipped`. Report those rows as 未取得結論 and never as passes.
|
Map the report labels to the template as `ok` -> `通過`, `default` -> `走預設`, `unset` -> `未設定`, `missing` -> `缺漏`, `invalid` -> `設錯`, and `skipped` -> `略過`.
|
||||||
|
|
||||||
Also run `tools/scan-config.sh orphans` — variables used in the source but absent from the spec table. They are a maintenance note for the skill set, not a fault on this machine.
|
Add `-o` when Gitea is unreachable; the Gitea-dependent rows then come back `skipped`. Report those rows as inconclusive and never as passes.
|
||||||
|
|
||||||
|
Also run `tools/scan-config.sh orphans` - variables used in the source but absent from the spec table. They are a maintenance note for the skill set, not a fault on this machine.
|
||||||
|
|
||||||
Done when the summary line is read and every `missing` and `invalid` row is named.
|
Done when the summary line is read and every `missing` and `invalid` row is named.
|
||||||
|
|
||||||
## 4. Own settings
|
## 5. Own settings
|
||||||
|
|
||||||
Run `tools/scan-config.sh scan project` from the current working directory. Same output format, `scope=project` rows only.
|
Run `tools/scan-config.sh scan project` from the current working directory. Same output format, `scope=project` rows only.
|
||||||
|
|
||||||
@@ -49,23 +68,23 @@ When `.env` or `.envrc` exists, name the spec-table variables it overrides and s
|
|||||||
|
|
||||||
Done when the scanned directory is stated and every project row has a verdict.
|
Done when the scanned directory is stated and every project row has a verdict.
|
||||||
|
|
||||||
## 5. Report and record
|
## 6. Report and record
|
||||||
|
|
||||||
Report all four tables per `templates/check-page.md`. Then build the 待修項目 table from every `missing`, `invalid` and `unwired` item, plus every domain reported 落後. Order them `missing` → `invalid` → `unwired` → `落後`. Nothing wrong → one row reading 無.
|
Report all five tables per `templates/check-page.md`. Then build the `待修項目` table from every `missing`, `invalid`, `unwired` and CLI runtime `fail` item, plus every domain reported `落後`. Order them `missing` -> `invalid` -> `unwired` -> `runtime-fail` -> `落後`. Nothing wrong -> one row reading `無`.
|
||||||
|
|
||||||
Write the page through `jsc-gitea:wiki`:
|
Write the page through `jsc-gitea:wiki`:
|
||||||
|
|
||||||
- Wiki repo: `jsc-gitea/tools/gitea.sh wiki-repo CHECK`.
|
- Wiki repo: `jsc-gitea/tools/gitea.sh wiki-repo CHECK`.
|
||||||
- Page name: `CHECK_` plus `gitea.sh hash-id "{hostname}/{user}"` — the host and the login account, not `{owner}/{repo}`. Doctor checks a machine, and it has to work in directories that are not repositories at all.
|
- Page name: `CHECK_` plus `gitea.sh hash-id "{hostname}/{user}"` - the host and the login account, not `{owner}/{repo}`. Doctor checks a machine, and it has to work in directories that are not repositories at all.
|
||||||
- Overwrite the whole page. This page type keeps only the latest run.
|
- Overwrite the whole page. This page type keeps only the latest run.
|
||||||
- Update `CHECK_CONTENTS` from `templates/check-contents.md` in the same pass.
|
- Update `CHECK_CONTENTS` from `templates/check-contents.md` in the same pass.
|
||||||
|
|
||||||
`wiki-repo` exiting 3 means no wiki repo is configured for CHECK. Print the tables, skip the wiki write, and put `JSC_WIKI_REPO_CHECK` at the top of 待修項目 — that unset variable is itself a finding, so a failed write never fails the health check.
|
`wiki-repo` exiting 3 means no wiki repo is configured for CHECK. Print the tables, skip the wiki write, and put `JSC_WIKI_REPO_CHECK` at the top of the `待修項目` table - that unset variable is itself a finding, so a failed write never fails the health check.
|
||||||
|
|
||||||
Done when either the wiki page URL is reported, or the skipped write is reported together with the reason.
|
Done when either the wiki page URL is reported, or the skipped write is reported together with the reason.
|
||||||
|
|
||||||
## 6. Hand off
|
## 7. Hand off
|
||||||
|
|
||||||
State the counts: required items missing, settings invalid, CLIs unwired, domains behind. Recommend `/jsc-cli:setup` when any of those is above zero. Never fix anything here.
|
State the counts: required items missing, settings invalid, CLIs unwired, CLI runtime failures, domains behind. Recommend `/jsc-cli:setup` when any of those is above zero. Never fix anything here.
|
||||||
|
|
||||||
Done when the counts are stated and the recommendation is given or explicitly withheld.
|
Done when the counts are stated and the recommendation is given or explicitly withheld.
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
> 由 `jsc-cli:doctor` 維護。每台執行環境一列;`HASH` 取 `{主機名}/{登入帳號}`,算法與其他頁面共用。
|
> 由 `jsc-cli:doctor` 維護。每台執行環境一列;`HASH` 取 `{主機名}/{登入帳號}`,算法與其他頁面共用。
|
||||||
|
|
||||||
| 體檢頁 | 主機 | 帳號 | 必要項缺漏 | 設定錯誤 | 最後體檢 |
|
| 體檢頁 | 主機 | 帳號 | 必要項缺漏 | 設定錯誤 | CLI 實測失敗 | 最後體檢 |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
| [[CHECK_{HASH}]] | {hostname} | {使用者帳號} | {n} | {n} | {yyyy-MM-dd HH:mm} |
|
| [[CHECK_{HASH}]] | {hostname} | {使用者帳號} | {n} | {n} | {n} | {yyyy-MM-dd HH:mm} |
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| 技能版本 | {n} | {n} | {n} |
|
| 技能版本 | {n} | {n} | {n} |
|
||||||
| Hook 接線 | {n} | {n} | {n} |
|
| Hook 接線 | {n} | {n} | {n} |
|
||||||
|
| CLI 實測 | {n} | {n} | {n} |
|
||||||
| 全域設定 | {n} | {n} | {n} |
|
| 全域設定 | {n} | {n} | {n} |
|
||||||
| 自我設定 | {n} | {n} | {n} |
|
| 自我設定 | {n} | {n} | {n} |
|
||||||
|
|
||||||
@@ -28,6 +29,12 @@
|
|||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| {cli} | {wired、degraded、unwired、skipped} | {項目名,逗號分隔;無則寫「無」} | {降級原因或未偵測到執行檔} |
|
| {cli} | {wired、degraded、unwired、skipped} | {項目名,逗號分隔;無則寫「無」} | {降級原因或未偵測到執行檔} |
|
||||||
|
|
||||||
|
## CLI 實測
|
||||||
|
|
||||||
|
| CLI | 測試 | 指令 | 結束碼 | 判定 | 說明 |
|
||||||
|
| --- | --- | --- | --- | --- | --- |
|
||||||
|
| {cli} | {version、help、plugin-list} | {實際命令} | {結束碼} | {通過、降級、失敗、略過} | {輸出摘要或錯誤原因} |
|
||||||
|
|
||||||
## 全域設定
|
## 全域設定
|
||||||
|
|
||||||
| 項目 | 必要 | 現況 | 期望 | 修法 | 判定 |
|
| 項目 | 必要 | 現況 | 期望 | 修法 | 判定 |
|
||||||
@@ -48,7 +55,7 @@
|
|||||||
|
|
||||||
| 順序 | 項目 | 範圍 | 判定 | 修法 | 影響 |
|
| 順序 | 項目 | 範圍 | 判定 | 修法 | 影響 |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
| {n} | {變數、檔案、hook 或 domain} | {全域、自我} | {缺漏、設錯、未接線、落後} | {自動、詢問、手動} | {不修的話哪些技能跑不動} |
|
| {n} | {變數、檔案、hook、CLI 測試或 domain} | {全域、自我、CLI} | {缺漏、設錯、未接線、實測失敗、落後} | {自動、詢問、手動} | {不修的話哪些技能跑不動} |
|
||||||
|
|
||||||
## 未登錄變數
|
## 未登錄變數
|
||||||
|
|
||||||
|
|||||||
Executable
+158
@@ -0,0 +1,158 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# test-clis.sh — 實際呼叫已安裝的 AI CLI,找出靜態設定看不出的環境問題。
|
||||||
|
# 用法:
|
||||||
|
# test-clis.sh [cli...]
|
||||||
|
# 輸出(TSV):
|
||||||
|
# test<TAB>{cli}<TAB>{test}<TAB>{指令}<TAB>{結束碼}<TAB>{判定}<TAB>{說明}
|
||||||
|
# summary<TAB>{ok}<TAB>{warn}<TAB>{fail}<TAB>{skipped}
|
||||||
|
# 判定:
|
||||||
|
# ok 實際命令成功
|
||||||
|
# warn CLI 可用,但該功能在這支 CLI 或這個版本不是必要功能
|
||||||
|
# fail 命令失敗、逾時,或必要功能不存在
|
||||||
|
# skipped 未安裝或無法執行該項測試
|
||||||
|
# 結束碼:0=測試完成;2=用法錯誤;3=找不到 detect-clis.sh
|
||||||
|
set -u
|
||||||
|
|
||||||
|
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||||
|
DETECT="$HERE/detect-clis.sh"
|
||||||
|
TIMEOUT_SECONDS="${JSC_CLI_TEST_TIMEOUT:-10}"
|
||||||
|
ok_count=0
|
||||||
|
warn_count=0
|
||||||
|
fail_count=0
|
||||||
|
skipped_count=0
|
||||||
|
|
||||||
|
[ -f "$DETECT" ] || { echo "找不到 detect-clis.sh:$DETECT" >&2; exit 3; }
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "用法:test-clis.sh [claude|codex|copilot|antigravity|kiro ...]" >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
cli_bin() {
|
||||||
|
case "$1" in
|
||||||
|
antigravity) printf 'agy' ;;
|
||||||
|
kiro) printf 'kiro-cli' ;;
|
||||||
|
claude|codex|copilot) printf '%s' "$1" ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
emit() { # cli test command code verdict detail
|
||||||
|
printf 'test\t%s\t%s\t%s\t%s\t%s\t%s\n' "$1" "$2" "$3" "$4" "$5" "$6"
|
||||||
|
case "$5" in
|
||||||
|
ok) ok_count=$((ok_count + 1)) ;;
|
||||||
|
warn) warn_count=$((warn_count + 1)) ;;
|
||||||
|
fail) fail_count=$((fail_count + 1)) ;;
|
||||||
|
skipped) skipped_count=$((skipped_count + 1)) ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
run_capture() { # $@=command
|
||||||
|
out_file=$(mktemp) || return 125
|
||||||
|
err_file=$(mktemp) || { rm -f "$out_file"; return 125; }
|
||||||
|
if command -v timeout >/dev/null 2>&1; then
|
||||||
|
timeout "$TIMEOUT_SECONDS" "$@" >"$out_file" 2>"$err_file"
|
||||||
|
else
|
||||||
|
"$@" >"$out_file" 2>"$err_file"
|
||||||
|
fi
|
||||||
|
code=$?
|
||||||
|
text=$(cat "$out_file" "$err_file" 2>/dev/null | tr '\r\n\t' ' ' | sed 's/[[:space:]][[:space:]]*/ /g; s/^ //; s/ $//; s/.*TOKEN[^ ]*/[secret]/g' | cut -c1-180)
|
||||||
|
rm -f "$out_file" "$err_file"
|
||||||
|
RUN_CODE=$code
|
||||||
|
RUN_TEXT=${text:-無輸出}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
run_required() { # cli test command...
|
||||||
|
cli=$1
|
||||||
|
test_name=$2
|
||||||
|
shift 2
|
||||||
|
cmd_text="$*"
|
||||||
|
run_capture "$@"
|
||||||
|
code=$RUN_CODE
|
||||||
|
detail=$RUN_TEXT
|
||||||
|
if [ "$code" -eq 0 ]; then
|
||||||
|
emit "$cli" "$test_name" "$cmd_text" "$code" ok "$detail"
|
||||||
|
elif [ "$code" -eq 124 ]; then
|
||||||
|
emit "$cli" "$test_name" "$cmd_text" "$code" fail "命令逾時(${TIMEOUT_SECONDS} 秒)"
|
||||||
|
else
|
||||||
|
emit "$cli" "$test_name" "$cmd_text" "$code" fail "$detail"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_optional() { # cli test command...
|
||||||
|
cli=$1
|
||||||
|
test_name=$2
|
||||||
|
shift 2
|
||||||
|
cmd_text="$*"
|
||||||
|
run_capture "$@"
|
||||||
|
code=$RUN_CODE
|
||||||
|
detail=$RUN_TEXT
|
||||||
|
if [ "$code" -eq 0 ]; then
|
||||||
|
emit "$cli" "$test_name" "$cmd_text" "$code" ok "$detail"
|
||||||
|
elif [ "$code" -eq 124 ]; then
|
||||||
|
emit "$cli" "$test_name" "$cmd_text" "$code" fail "命令逾時(${TIMEOUT_SECONDS} 秒)"
|
||||||
|
else
|
||||||
|
emit "$cli" "$test_name" "$cmd_text" "$code" warn "$detail"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
has_cli() {
|
||||||
|
name=$1
|
||||||
|
"$DETECT" | awk -F '\t' -v name="$name" '$1 == name { found = 1 } END { exit found ? 0 : 1 }'
|
||||||
|
}
|
||||||
|
|
||||||
|
test_one() {
|
||||||
|
cli=$1
|
||||||
|
bin=$(cli_bin "$cli") || usage
|
||||||
|
path=$(command -v "$bin" 2>/dev/null || true)
|
||||||
|
if [ -z "$path" ]; then
|
||||||
|
emit "$cli" executable "$bin" "-" skipped "未偵測到執行檔"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$cli" in
|
||||||
|
antigravity)
|
||||||
|
run_required "$cli" version "$path" --version
|
||||||
|
run_optional "$cli" help "$path" --help
|
||||||
|
run_required "$cli" plugin-list "$path" plugin list
|
||||||
|
;;
|
||||||
|
kiro)
|
||||||
|
run_required "$cli" version "$path" --version
|
||||||
|
run_optional "$cli" help "$path" --help-all
|
||||||
|
run_optional "$cli" plugin-list "$path" plugin list
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
run_required "$cli" version "$path" --version
|
||||||
|
run_optional "$cli" help "$path" --help
|
||||||
|
run_required "$cli" plugin-list "$path" plugin list
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
set -- $("$DETECT" | cut -f1)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
emit all executable "-" "-" skipped "未偵測到任何支援的 CLI"
|
||||||
|
printf 'summary\t%s\t%s\t%s\t%s\n' "$ok_count" "$warn_count" "$fail_count" "$skipped_count"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for cli in "$@"; do
|
||||||
|
case "$cli" in
|
||||||
|
claude|codex|copilot|antigravity|kiro)
|
||||||
|
if has_cli "$cli"; then
|
||||||
|
test_one "$cli"
|
||||||
|
else
|
||||||
|
bin=$(cli_bin "$cli") || usage
|
||||||
|
emit "$cli" executable "$bin" "-" skipped "未偵測到執行檔"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*) usage ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
printf 'summary\t%s\t%s\t%s\t%s\n' "$ok_count" "$warn_count" "$fail_count" "$skipped_count"
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user