Merge pull request '發佈 jsc-cli 0.0.4:模型能力標籤閘門與階段模型路由' (#6) from develop into master
Reviewed-on: #6 Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
This commit was merged in pull request #6.
This commit is contained in:
@@ -1,75 +1,89 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc",
|
"name": "jsc",
|
||||||
|
"description": "jsc 跨 AI 助理技能組的統一 marketplace(claude / codex / copilot / antigravity / kiro)。",
|
||||||
|
"owner": {
|
||||||
|
"name": "JSC"
|
||||||
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"name": "jsc-ask",
|
"name": "jsc-ask",
|
||||||
"source": {
|
"source": {
|
||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/ask.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/ask.git"
|
||||||
}
|
},
|
||||||
|
"description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-cli",
|
"name": "jsc-cli",
|
||||||
"source": {
|
"source": {
|
||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/cli.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/cli.git"
|
||||||
}
|
},
|
||||||
|
"description": "CLI 偵測、模型能力標籤與技能庫批次部署"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-git",
|
"name": "jsc-git",
|
||||||
"source": {
|
"source": {
|
||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/git.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/git.git"
|
||||||
}
|
},
|
||||||
|
"description": "Commit 分組認可與 Push Request 建立"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-gitea",
|
"name": "jsc-gitea",
|
||||||
"source": {
|
"source": {
|
||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/gitea.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/gitea.git"
|
||||||
}
|
},
|
||||||
|
"description": "Gitea API 工具、Wiki 讀寫與存取庫批次同步"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-hooks",
|
"name": "jsc-hooks",
|
||||||
"source": {
|
"source": {
|
||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/hooks.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/hooks.git"
|
||||||
}
|
},
|
||||||
|
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-log",
|
"name": "jsc-log",
|
||||||
"source": {
|
"source": {
|
||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/log.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/log.git"
|
||||||
}
|
},
|
||||||
|
"description": "工作日誌(LOG_* wiki 頁)與技能使用統計"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-meta",
|
"name": "jsc-meta",
|
||||||
"source": {
|
"source": {
|
||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/meta.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/meta.git"
|
||||||
}
|
},
|
||||||
|
"description": "技能組自我管理:新建、更新、刪除技能與技能準則"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-pkg",
|
"name": "jsc-pkg",
|
||||||
"source": {
|
"source": {
|
||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/pkg.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/pkg.git"
|
||||||
}
|
},
|
||||||
|
"description": "套件批次更新(nodejs/python/dotnet),失敗還原"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-review",
|
"name": "jsc-review",
|
||||||
"source": {
|
"source": {
|
||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/review.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/review.git"
|
||||||
}
|
},
|
||||||
|
"description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-sdlc",
|
"name": "jsc-sdlc",
|
||||||
"source": {
|
"source": {
|
||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/sdlc.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/sdlc.git"
|
||||||
}
|
},
|
||||||
|
"description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@
|
|||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/meta.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/meta.git"
|
||||||
},
|
},
|
||||||
"description": "技能組自我管理:新建/更新/刪除技能與技能準則"
|
"description": "技能組自我管理:新建、更新、刪除技能與技能準則"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-pkg",
|
"name": "jsc-pkg",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-cli",
|
"name": "jsc-cli",
|
||||||
"version": "0.0.1",
|
"version": "0.0.4",
|
||||||
"description": "CLI 偵測、模型能力標籤與技能庫批次部署",
|
"description": "CLI 偵測、模型能力標籤與技能庫批次部署",
|
||||||
"skills": "./skills",
|
"skills": "./skills",
|
||||||
"author": {
|
"author": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-cli",
|
"name": "jsc-cli",
|
||||||
"version": "0.0.1",
|
"version": "0.0.4",
|
||||||
"description": "CLI 偵測、模型能力標籤與技能庫批次部署",
|
"description": "CLI 偵測、模型能力標籤與技能庫批次部署",
|
||||||
"skills": "./skills"
|
"skills": "./skills"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/jsc.git),安
|
|||||||
| 工具 | 用途 |
|
| 工具 | 用途 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `tools/detect-clis.sh` | 列出已安裝的 AI CLI 與執行檔路徑(TSV:name / path / version;kiro 的執行檔為 `kiro-cli`) |
|
| `tools/detect-clis.sh` | 列出已安裝的 AI CLI 與執行檔路徑(TSV:name / path / version;kiro 的執行檔為 `kiro-cli`) |
|
||||||
|
| `tools/model-config.sh` | 解析 SDLC 各階段的偏好模型鏈(`get {stage}`、`list`、`resolve {stage}` 印出目前 CLI 可用的第一個模型);專案 `.jsc/models` 優先於 `$JSC_HOME/models.conf`,格式見 `references/model-tags.md`。鏈只影響建議與偏好順序,不影響閘門放行 |
|
||||||
|
| `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 目錄
|
## Skills 目錄
|
||||||
|
|
||||||
@@ -30,7 +32,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/jsc.git),安
|
|||||||
|
|
||||||
### `models`
|
### `models`
|
||||||
|
|
||||||
讀取各已安裝 CLI 可使用的模型並加上能力標籤(`references/model-tags.md`),並列出 SDLC 各階段的必要標籤,供 `jsc-sdlc` 的模型能力閘門使用。
|
讀取各已安裝 CLI 可使用的模型並加上能力標籤(`references/model-tags.md`),用 `tools/model-tags.sh sync` 把標籤表寫進 `$JSC_HOME/model-tags.tsv` 供 sdlc-gate 讀取,列出 SDLC 各階段的必要標籤(plan、analyze 需 `reasoning-max`;implement 需 `coding`;maintain 任意),並用 `tools/model-config.sh` 列出各階段的偏好模型鏈。`jsc-sdlc` 閘門一律以能力標籤判定,偏好鏈只用來建議切換目標。
|
||||||
|
|
||||||
### `deploy`
|
### `deploy`
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-cli",
|
"name": "jsc-cli",
|
||||||
"version": "0.0.1",
|
"version": "0.0.4",
|
||||||
"description": "CLI 偵測、模型能力標籤與技能庫批次部署",
|
"description": "CLI 偵測、模型能力標籤與技能庫批次部署",
|
||||||
"skills": "./skills/"
|
"skills": "./skills/"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
| 標籤 | 意義 |
|
| 標籤 | 意義 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| reasoning-high | 高階推理,適合規劃與分析 |
|
| reasoning-max | 頂級推理,各家當代最強一階;需求拆解、架構決策、跨檔案分析 |
|
||||||
|
| reasoning-high | 高階推理,足以判讀規格與寫實作 |
|
||||||
| coding | 程式碼產生與修改能力佳 |
|
| coding | 程式碼產生與修改能力佳 |
|
||||||
| fast | 低延遲 |
|
| fast | 低延遲 |
|
||||||
| cheap | 低成本 |
|
| cheap | 低成本 |
|
||||||
@@ -15,23 +16,68 @@
|
|||||||
|
|
||||||
| 模型 | 標籤 |
|
| 模型 | 標籤 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| claude-fable-5 | reasoning-high, coding, long-context, vision |
|
| claude-fable-5 | reasoning-max, reasoning-high, coding, long-context, vision |
|
||||||
| claude-opus-5 | reasoning-high, coding, long-context, vision |
|
| claude-opus-5 | reasoning-max, reasoning-high, coding, long-context, vision |
|
||||||
| claude-sonnet-5 | reasoning-high, coding, fast, long-context, vision |
|
| claude-sonnet-5 | reasoning-high, coding, fast, long-context, vision |
|
||||||
| claude-haiku-4-5 | coding, fast, cheap, long-context, vision |
|
| claude-haiku-4-5 | coding, fast, cheap, long-context, vision |
|
||||||
| gpt-5.x / o 系列(codex 預設) | reasoning-high, coding, long-context, vision |
|
| gpt-5.x / o 系列(codex 預設) | reasoning-max, reasoning-high, coding, long-context, vision |
|
||||||
| gpt-5.x-mini / codex-mini | coding, fast, cheap |
|
| gpt-5.x-mini / codex-mini | coding, fast, cheap |
|
||||||
| gemini-3-pro | reasoning-high, coding, long-context, vision |
|
| gemini-3-pro | reasoning-max, reasoning-high, coding, long-context, vision |
|
||||||
| gemini-3-flash | coding, fast, cheap, long-context, vision |
|
| gemini-3-flash | coding, fast, cheap, long-context, vision |
|
||||||
| copilot 內建(gpt/claude 選項) | 依所選底層模型比照上表 |
|
| copilot 內建(gpt/claude 選項) | 依所選底層模型比照上表 |
|
||||||
|
|
||||||
|
標籤蘊含關係:`reasoning-max` 是 `reasoning-high` 的上位標籤。補表時,帶 `reasoning-max` 的模型**必須同時列出 `reasoning-high`**——比對是逐標籤字面比對,不推導上下位關係,漏列會讓該模型卡在只要求 `reasoning-high` 的階段。
|
||||||
|
|
||||||
## SDLC 階段需求
|
## SDLC 階段需求
|
||||||
|
|
||||||
| 階段 | 必要標籤 |
|
| 階段 | 必要標籤 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 規劃(plan) | reasoning-high |
|
| 規劃(plan) | reasoning-max |
|
||||||
| 分析(analyze) | reasoning-high |
|
| 分析(analyze) | reasoning-max |
|
||||||
| 實作(implement) | coding |
|
| 實作(implement) | coding |
|
||||||
| 維護(maintain) | 任意 |
|
| 維護(maintain) | 任意 |
|
||||||
|
|
||||||
|
規劃與分析要求 `reasoning-max`,實作只要求 `coding`:需求拆解與架構決策一步走錯,後面整批工作包都跟著錯,成本遠高於實作階段的單點失誤。
|
||||||
|
|
||||||
|
### 判定方式(程式層,不由模型自評)
|
||||||
|
|
||||||
|
閘門一律由腳本判定,**模型不得自行宣稱具備標籤**:
|
||||||
|
|
||||||
|
| 工具 | 職責 |
|
||||||
|
| --- | --- |
|
||||||
|
| `jsc-cli/tools/model-tags.sh gate {階段} {模型 id}` | 查上面兩張表,印 `PASS`/`FAIL:{缺少的標籤}`/`UNKNOWN-MODEL`;exit code 0/1/2 |
|
||||||
|
| `jsc-cli/tools/model-tags.sh sync` | 把兩張表寫成 `$JSC_HOME/model-tags.tsv`,供 hook 讀取 |
|
||||||
|
| `jsc-hooks/hooks/sdlc-gate.sh lock {階段}` | 從 transcript 讀出**實際**模型 id,比對必要標籤;不符就拒絕上鎖並回報 |
|
||||||
|
| `jsc-hooks/hooks/sdlc-gate.sh check` | 每次送出提示時比對;不符即擋下該輪提示(exit 2) |
|
||||||
|
|
||||||
|
模型 id 一律取自 transcript 記錄的實際值,不採用模型自我回報——自我回報無法驗證,等同沒有閘門。
|
||||||
|
|
||||||
|
## 階段指定模型(角色式路由)
|
||||||
|
|
||||||
|
每個 SDLC 階段可以指定一個模型,並附上遞補模型。設定格式一行一階段:
|
||||||
|
|
||||||
|
```
|
||||||
|
stage=model[,fallback1[,fallback2...]]
|
||||||
|
```
|
||||||
|
|
||||||
|
「#」開頭為註解,空白行忽略。階段限定 plan、analyze、implement、maintain。
|
||||||
|
|
||||||
|
設定檔位置與優先序:
|
||||||
|
|
||||||
|
| 順位 | 檔案 | 範圍 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 1 | 專案目錄的 `.jsc/models` | 專案覆寫 |
|
||||||
|
| 2 | `$JSC_HOME/models.conf`(`JSC_HOME` 預設 `~/.jsc`) | 全域 |
|
||||||
|
|
||||||
|
專案檔有設定的階段完全覆寫全域設定;其餘階段仍讀全域檔。解析一律用 `jsc-cli/tools/model-config.sh`,不自行讀檔。
|
||||||
|
|
||||||
|
遞補鏈語意:鏈上第一個模型優先;前面的模型不可用時依序後退到下一個。
|
||||||
|
|
||||||
|
**標籤是閘門的唯一判準**:階段能不能跑,一律由「SDLC 階段需求」的必要標籤決定。指定模型鏈不參與放行判定,只有兩個用途:
|
||||||
|
|
||||||
|
1. 閘門擋下時,指出建議切換到哪一個模型(鏈上第一個滿足該階段必要標籤的模型)。
|
||||||
|
2. 同樣合格的模型有多個時,表達偏好順序。
|
||||||
|
|
||||||
|
因此「指定鏈上的模型」若不具備該階段必要標籤,照樣被擋;設定檔不能當成繞過閘門的後門。
|
||||||
|
|
||||||
表列以外的模型:依 `jsc-ask:ask` 規則詢問使用者或依供應商文件補表後再判定。
|
表列以外的模型:依 `jsc-ask:ask` 規則詢問使用者或依供應商文件補表後再判定。
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: models
|
name: models
|
||||||
description: List every model usable by each installed AI CLI (claude, codex, copilot, antigravity, kiro) and attach capability tags from references/model-tags.md. Also states which tags each SDLC phase requires, so jsc-sdlc skills can gate execution. Use when checking model fitness or inventorying models; not for switching models.
|
description: List every model usable by each installed AI CLI (claude, codex, copilot, antigravity, kiro) and attach capability tags from references/model-tags.md. Syncs the tag table to $JSC_HOME/model-tags.tsv via tools/model-tags.sh so jsc-sdlc gates can be enforced in code, states which tags each SDLC stage requires (plan and analyze need reasoning-max, implement needs coding, maintain any), and resolves each stage's preferred model chain via tools/model-config.sh (project .jsc/models overrides $JSC_HOME/models.conf) for switch suggestions only. Use when checking model fitness, inventorying models, or reviewing stage gating; not for switching models or editing the config files.
|
||||||
---
|
---
|
||||||
|
|
||||||
# models — list CLI models with capability tags
|
# models — list CLI models with capability tags
|
||||||
@@ -21,4 +21,6 @@ description: List every model usable by each installed AI CLI (claude, codex, co
|
|||||||
If a config is unreadable, list that CLI's known default models and mark each one with the literal label 「預設推定」 (assumed default).
|
If a config is unreadable, list that CLI's known default models and mark each one with the literal label 「預設推定」 (assumed default).
|
||||||
3. Attach capability tags to every model per `references/model-tags.md`. Handle unlisted models per the closing rule of that file.
|
3. Attach capability tags to every model per `references/model-tags.md`. Handle unlisted models per the closing rule of that file.
|
||||||
4. Output a table with four columns: CLI, model, tags, currently in use.
|
4. Output a table with four columns: CLI, model, tags, currently in use.
|
||||||
5. Append the SDLC phase requirement table (plan and analyze need `reasoning-high`; implement needs `coding`; maintain accepts any), so `jsc-sdlc` skills can gate on model capability.
|
5. Run `tools/model-tags.sh sync` to write the tag table to `$JSC_HOME/model-tags.tsv`, and report the path. This file is what `jsc-hooks/hooks/sdlc-gate.sh` reads, so the SDLC gate stays broken until it exists. Done when the command prints the path.
|
||||||
|
6. Append the SDLC stage requirement table (plan and analyze need `reasoning-max`; implement needs `coding`; maintain accepts any), and state that gating is done in code by `sdlc-gate.sh lock {stage}` against the transcript's actual model id — **the models listed here are never allowed to self-assess their own tags**.
|
||||||
|
7. Run `jsc-cli/tools/model-config.sh list` and append a 「階段偏好模型」 table right after the stage requirement table, with three columns: stage, chain, source (`project` / `global`). State below the table that the chain does **not** grant passage: it only names the model to suggest switching to when the gate blocks, and expresses preference among models that already satisfy the required tags. Done when the table shows all four stages, with `-` for unconfigured ones.
|
||||||
|
|||||||
Executable
+122
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# model-config.sh — 解析 SDLC 各階段的指定模型鏈(角色式路由)。
|
||||||
|
# 設定格式(一行一階段):stage=model[,fallback1[,fallback2...]]
|
||||||
|
# 「#」開頭為註解,空白行忽略。階段限定:plan、analyze、implement、maintain。
|
||||||
|
# 解析順序:
|
||||||
|
# 1. 目前工作目錄的 ./.jsc/models(專案覆寫)
|
||||||
|
# 2. $JSC_HOME/models.conf(JSC_HOME 預設 ~/.jsc)
|
||||||
|
# 專案檔有設定的階段完全覆寫全域設定;其餘階段仍讀全域檔。
|
||||||
|
# 用法:
|
||||||
|
# model-config.sh get {stage} 印出該階段解析後的模型鏈(逗號分隔);未設定不印。兩種情況都 exit 0。
|
||||||
|
# model-config.sh list 每階段一行:stage<TAB>chain<TAB>source(project 或 global);未設定的 chain 與 source 印「-」。
|
||||||
|
# model-config.sh resolve {stage} 印出該階段「目前 CLI 可用的第一個模型」單一名稱;鏈為空或無法判定時不印,exit 0。
|
||||||
|
set -u
|
||||||
|
|
||||||
|
JSC_HOME="${JSC_HOME:-$HOME/.jsc}"
|
||||||
|
PROJECT_FILE=./.jsc/models
|
||||||
|
GLOBAL_FILE="$JSC_HOME/models.conf"
|
||||||
|
STAGES="plan analyze implement maintain"
|
||||||
|
|
||||||
|
# 取出單一設定檔裡某階段的模型鏈。
|
||||||
|
# 同階段出現多行時取最後一行,結果才有確定性。
|
||||||
|
read_chain() { # $1=設定檔 $2=階段
|
||||||
|
[ -f "$1" ] || return 0
|
||||||
|
awk -F= -v stage="$2" '
|
||||||
|
{ sub(/\r$/, "") } # 去掉 CR
|
||||||
|
/^[ \t]*#/ { next } # 註解行
|
||||||
|
/^[ \t]*$/ { next } # 空白行
|
||||||
|
index($0, "=") == 0 { next }
|
||||||
|
{
|
||||||
|
key = $1
|
||||||
|
gsub(/[ \t]/, "", key)
|
||||||
|
if (key != stage) next
|
||||||
|
val = substr($0, index($0, "=") + 1)
|
||||||
|
gsub(/[ \t]/, "", val)
|
||||||
|
if (val != "") chain = val
|
||||||
|
}
|
||||||
|
END { if (chain != "") print chain }
|
||||||
|
' "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 解析單一階段:專案檔優先,該階段沒設定才退回全域檔。
|
||||||
|
# 輸出:chain<TAB>source;兩檔都沒設定時不輸出。
|
||||||
|
resolve() { # $1=階段
|
||||||
|
chain=$(read_chain "$PROJECT_FILE" "$1")
|
||||||
|
if [ -n "$chain" ]; then
|
||||||
|
printf '%s\tproject\n' "$chain"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
chain=$(read_chain "$GLOBAL_FILE" "$1")
|
||||||
|
if [ -n "$chain" ]; then
|
||||||
|
printf '%s\tglobal\n' "$chain"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 判斷目前是哪個 CLI 在跑(盡量而為,非必要)。
|
||||||
|
# 依 CLI 官方常見環境變數判斷;偵測不到就回傳 unknown。
|
||||||
|
current_cli() {
|
||||||
|
[ -n "${CLAUDECODE:-}" ] && { echo claude; return 0; }
|
||||||
|
[ -n "${CODEX_SANDBOX:-}${CODEX_HOME:-}" ] && { echo codex; return 0; }
|
||||||
|
[ -n "${COPILOT_AGENT:-}" ] && { echo copilot; return 0; }
|
||||||
|
[ -n "${ANTIGRAVITY_SESSION:-}" ] && { echo antigravity; return 0; }
|
||||||
|
[ -n "${KIRO_SESSION:-}" ] && { echo kiro; return 0; }
|
||||||
|
echo unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
# resolve 子指令:印出「目前 CLI 可用的第一個模型」單一名稱。
|
||||||
|
# 簡化說明:這個 repo 目前沒有「列出某 CLI 實際安裝/可用模型」的 shell 級機制
|
||||||
|
# ——現有的 skills/models 是靠 sub agent 讀各 CLI 的設定檔(~/.claude/settings.json、
|
||||||
|
# ~/.codex/config.toml 等),屬於 LLM 才能做的判讀,沒辦法在這支 POSIX sh 腳本裡重現。
|
||||||
|
# 因此這裡只用 current_cli() 抓到的「目前 CLI 名稱」這個既有訊號做記錄,
|
||||||
|
# 實際判斷可用性一律簡化為「直接取模型鏈的第一個模型」,不逐一檢查該模型是否真的能用。
|
||||||
|
# 之後若要做到依 CLI 實際可用模型過濾,可以在這裡比對 current_cli 的結果與
|
||||||
|
# references/model-tags.md/detect-clis.sh 的輸出,逐一嘗試鏈上模型直到找到可用的。
|
||||||
|
resolve_first_usable() { # $1=階段
|
||||||
|
chain=$(resolve "$1" | cut -f1)
|
||||||
|
[ -n "$chain" ] || return 0
|
||||||
|
cli=$(current_cli)
|
||||||
|
: "目前偵測到的 CLI:$cli(本版本尚未用來篩選模型,見上方簡化說明)"
|
||||||
|
printf '%s\n' "$chain" | cut -d, -f1
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "用法:model-config.sh get {plan|analyze|implement|maintain} | list | resolve {plan|analyze|implement|maintain}" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
get)
|
||||||
|
stage="${2:-}"
|
||||||
|
case " $STAGES " in
|
||||||
|
*" $stage "*) ;;
|
||||||
|
*) usage ;;
|
||||||
|
esac
|
||||||
|
resolve "$stage" | cut -f1
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
resolve)
|
||||||
|
stage="${2:-}"
|
||||||
|
case " $STAGES " in
|
||||||
|
*" $stage "*) ;;
|
||||||
|
*) usage ;;
|
||||||
|
esac
|
||||||
|
resolve_first_usable "$stage"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
list)
|
||||||
|
for stage in $STAGES; do
|
||||||
|
line=$(resolve "$stage")
|
||||||
|
if [ -n "$line" ]; then
|
||||||
|
printf '%s\t%s\n' "$stage" "$line"
|
||||||
|
else
|
||||||
|
printf '%s\t-\t-\n' "$stage"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Executable
+155
@@ -0,0 +1,155 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# model-tags.sh — 解析「模型能力標籤」與「SDLC 階段必要標籤」,並判定模型是否夠格跑某階段。
|
||||||
|
#
|
||||||
|
# 資料來源優先序(前一項可取得就不往下):
|
||||||
|
# 1. $JSC_HOME/model-tags.tsv(機器可讀快取,可手改,用來補表列以外的模型)
|
||||||
|
# 2. 本 plugin 的 references/model-tags.md(唯一權威來源,不在腳本內另存一份對照表)
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# model-tags.sh dump 把對照表印成 TSV,不寫檔
|
||||||
|
# model-tags.sh sync 把對照表寫進 $JSC_HOME/model-tags.tsv(給 hook 讀)
|
||||||
|
# model-tags.sh stage {stage} 印出該階段的必要標籤(逗號分隔);階段不限標籤印 any
|
||||||
|
# model-tags.sh model {model-id} 印出該模型的標籤(逗號分隔);查不到不印
|
||||||
|
# model-tags.sh gate {stage} {model-id} 判定該模型能否跑該階段
|
||||||
|
#
|
||||||
|
# TSV 欄位(三欄,Tab 分隔):
|
||||||
|
# stage<TAB>{階段}<TAB>{必要標籤以逗號分隔,不限標籤時為 any}
|
||||||
|
# model<TAB>{模型鍵}<TAB>{能力標籤以逗號分隔}
|
||||||
|
#
|
||||||
|
# gate 的輸出與 exit code:
|
||||||
|
# PASS exit 0 模型具備該階段全部必要標籤
|
||||||
|
# FAIL:{缺少的標籤} exit 1 模型缺標籤,不得執行該階段
|
||||||
|
# UNKNOWN-MODEL exit 2 模型不在對照表上,無法判定
|
||||||
|
# UNKNOWN-STAGE exit 2 階段不在對照表上,無法判定
|
||||||
|
set -u
|
||||||
|
|
||||||
|
HERE=$(dirname "$0")
|
||||||
|
REF="$HERE/../references/model-tags.md"
|
||||||
|
JSC_HOME="${JSC_HOME:-$HOME/.jsc}"
|
||||||
|
TSV="$JSC_HOME/model-tags.tsv"
|
||||||
|
|
||||||
|
# 解析 references/model-tags.md 的兩張表格,輸出 TSV。
|
||||||
|
# 「## 對照表」段落 → model 列;「## SDLC 階段需求」段落 → stage 列。
|
||||||
|
# 模型欄可能寫成「gpt-5.x / o 系列(codex 預設)」這種人讀格式,
|
||||||
|
# 因此先砍掉全形括號後的說明,再用「/」切成多個鍵,只留純 ASCII 的鍵。
|
||||||
|
parse_ref() {
|
||||||
|
[ -f "$REF" ] || return 0
|
||||||
|
awk '
|
||||||
|
function trim(s) { sub(/^[ \t]+/, "", s); sub(/[ \t]+$/, "", s); return s }
|
||||||
|
# 砍掉第一個全形左括號之後的內容(表中括號都是尾端的補充說明)。
|
||||||
|
# 一律用 split 切字串,不用 substr 算位移——全形字元的長度在不同 locale 下
|
||||||
|
# 會被算成 1 個字元或 3 個 byte,位移法會在其中一種 locale 下算錯。
|
||||||
|
function strip_note(s) { split(s, a, "("); return trim(a[1]) }
|
||||||
|
# 取全形括號內的文字(例:規劃(plan) → plan)
|
||||||
|
function paren(s) {
|
||||||
|
if (split(s, a, "(") < 2) return ""
|
||||||
|
split(a[2], b, ")")
|
||||||
|
return trim(b[1])
|
||||||
|
}
|
||||||
|
function is_ascii_key(s) { return s ~ /^[A-Za-z0-9._-]+$/ }
|
||||||
|
function norm_tags(s) { gsub(/[ \t]/, "", s); return s }
|
||||||
|
|
||||||
|
/^##[ \t]/ {
|
||||||
|
if (index($0, "對照表") > 0) sect = "model"
|
||||||
|
else if (index($0, "SDLC 階段需求") > 0) sect = "stage"
|
||||||
|
else sect = ""
|
||||||
|
next
|
||||||
|
}
|
||||||
|
sect == "" { next }
|
||||||
|
/^\|/ {
|
||||||
|
line = $0
|
||||||
|
sub(/^\|/, "", line); sub(/\|[ \t]*$/, "", line)
|
||||||
|
n = split(line, f, "|")
|
||||||
|
if (n < 2) next
|
||||||
|
c1 = trim(f[1]); c2 = trim(f[2])
|
||||||
|
if (c1 == "" || c2 == "") next
|
||||||
|
if (c1 ~ /^:?-+:?$/) next # 表頭分隔列
|
||||||
|
if (c1 == "模型" || c1 == "階段" || c1 == "標籤") next # 表頭
|
||||||
|
|
||||||
|
if (sect == "stage") {
|
||||||
|
key = paren(c1)
|
||||||
|
if (key == "") key = c1
|
||||||
|
if (!is_ascii_key(key)) next
|
||||||
|
tags = (index(c2, "任意") > 0) ? "any" : norm_tags(c2)
|
||||||
|
printf "stage\t%s\t%s\n", key, tags
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
tags = norm_tags(c2)
|
||||||
|
if (tags == "") next
|
||||||
|
m = split(strip_note(c1), keys, "/")
|
||||||
|
for (i = 1; i <= m; i++) {
|
||||||
|
k = trim(keys[i])
|
||||||
|
if (k == "" || !is_ascii_key(k)) continue
|
||||||
|
printf "model\t%s\t%s\n", k, tags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$REF"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 取得對照表內容:快取優先,沒有快取才解析 md。
|
||||||
|
data() {
|
||||||
|
if [ -s "$TSV" ]; then
|
||||||
|
cat "$TSV"
|
||||||
|
else
|
||||||
|
parse_ref
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stage_tags() { # $1=階段
|
||||||
|
data | awk -F'\t' -v s="$1" '$1 == "stage" && $2 == s { print $3; exit }'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 模型鍵與實際模型 id 雙向包含即視為同一家族。
|
||||||
|
# 例:表列 claude-haiku-4-5 對得上實際 id claude-haiku-4-5-20251001。
|
||||||
|
# 多筆命中時取最長鍵,避免 claude 這種短鍵蓋掉精確鍵。
|
||||||
|
model_tags() { # $1=模型 id
|
||||||
|
data | awk -F'\t' -v m="$1" '
|
||||||
|
$1 == "model" && (index(m, $2) > 0 || index($2, m) > 0) {
|
||||||
|
if (length($2) > best_len) { best_len = length($2); best = $3 }
|
||||||
|
}
|
||||||
|
END { if (best != "") print best }
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
gate() { # $1=階段 $2=模型 id
|
||||||
|
req=$(stage_tags "$1")
|
||||||
|
if [ -z "$req" ]; then echo "UNKNOWN-STAGE"; return 2; fi
|
||||||
|
[ "$req" = "any" ] && { echo "PASS"; return 0; }
|
||||||
|
have=$(model_tags "$2")
|
||||||
|
if [ -z "$have" ]; then echo "UNKNOWN-MODEL"; return 2; fi
|
||||||
|
missing=$(awk -v req="$req" -v have="$have" '
|
||||||
|
BEGIN {
|
||||||
|
n = split(have, h, ","); for (i = 1; i <= n; i++) owned[h[i]] = 1
|
||||||
|
n = split(req, r, ",")
|
||||||
|
for (i = 1; i <= n; i++) if (!(r[i] in owned)) out = (out == "" ? r[i] : out "," r[i])
|
||||||
|
print out
|
||||||
|
}')
|
||||||
|
if [ -n "$missing" ]; then echo "FAIL:$missing"; return 1; fi
|
||||||
|
echo "PASS"; return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "用法:model-tags.sh dump | sync | stage {階段} | model {模型 id} | gate {階段} {模型 id}" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-}" in
|
||||||
|
dump) parse_ref; exit 0 ;;
|
||||||
|
sync)
|
||||||
|
out=$(parse_ref)
|
||||||
|
if [ -z "$out" ]; then
|
||||||
|
echo "[jsc] 解析不到 $REF,未寫出 $TSV" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p "$JSC_HOME" 2>/dev/null || true
|
||||||
|
printf '%s\n' "$out" > "$TSV" || exit 1
|
||||||
|
echo "$TSV"
|
||||||
|
exit 0 ;;
|
||||||
|
stage) [ -n "${2:-}" ] || usage; stage_tags "$2"; exit 0 ;;
|
||||||
|
model) [ -n "${2:-}" ] || usage; model_tags "$2"; exit 0 ;;
|
||||||
|
gate)
|
||||||
|
[ -n "${2:-}" ] && [ -n "${3:-}" ] || usage
|
||||||
|
gate "$2" "$3"; exit $? ;;
|
||||||
|
*) usage ;;
|
||||||
|
esac
|
||||||
Reference in New Issue
Block a user