From 05b37791189c5ab38a338fa75ab65095e468c57f Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 24 Aug 2026 11:36:05 +0800 Subject: [PATCH 01/10] =?UTF-8?q?feat(sdlc-gate):=20=E6=96=B0=E5=A2=9E=20S?= =?UTF-8?q?DLC=20=E6=AF=8F=20session=20=E6=A8=A1=E5=9E=8B=E9=8E=96=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:新增 hooks/sdlc-gate.sh,提供 lock、unlock、check、report 四個子指令;hooks.json 於 UserPromptSubmit 加掛 check。 Why:SDLC 各階段對模型能力有硬性要求,僅靠技能提示無法防止中途切換模型,需要 hook 層強制。 How:以 session 為鍵把「階段 模型」寫入 $JSC_HOME/sessions/{sid}.stage;check 依 stdin JSON、JSC_MODEL、~/.claude/settings.json 順序判定目前模型,別名互含視為相符,不符時注入繁中封鎖提醒;缺資料一律安靜降級、永遠 exit 0。 Who:jsc-sdlc 四個階段技能(plan、analyze、implement、maintain)於閘門通過時上鎖,Claude Code 的 UserPromptSubmit hook 負責攔查。 Co-Authored-By: Claude Fable 5 --- hooks/hooks.json | 5 ++++- hooks/sdlc-gate.sh | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100755 hooks/sdlc-gate.sh diff --git a/hooks/hooks.json b/hooks/hooks.json index 729bf36..11cc543 100644 --- a/hooks/hooks.json +++ b/hooks/hooks.json @@ -4,7 +4,10 @@ { "hooks": [ { "type": "command", "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/session-timer.sh\" start" } ] } ], "UserPromptSubmit": [ - { "hooks": [ { "type": "command", "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/ste100-guard.sh\"" } ] } + { "hooks": [ + { "type": "command", "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/ste100-guard.sh\"" }, + { "type": "command", "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/sdlc-gate.sh\" check" } + ] } ], "Stop": [ { "hooks": [ { "type": "command", "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/session-timer.sh\" mark" } ] } diff --git a/hooks/sdlc-gate.sh b/hooks/sdlc-gate.sh new file mode 100755 index 0000000..cb1860f --- /dev/null +++ b/hooks/sdlc-gate.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env sh +# sdlc-gate.sh — SDLC 每 session 模型鎖(stage ∈ plan/analyze/implement/maintain)。 +# 狀態檔: $JSC_HOME/sessions/{sid}.stage,單行「{stage} {model}」。 +# 用法: +# sdlc-gate.sh lock {stage} {model} # 階段閘門通過時上鎖(覆寫既有鎖) +# sdlc-gate.sh unlock # 移除狀態檔 +# sdlc-gate.sh check # hook 模式(UserPromptSubmit):模型不符時注入繁中提醒 +# sdlc-gate.sh report # 印出 {sid} {stage} {model};無鎖不印 +# 缺資料時安靜降級,永遠 exit 0,不可中斷宿主 CLI。 +HERE=$(dirname "$0"); . "$HERE/lib.sh" +read_stdin +sid=$(session_id) +state="$JSC_HOME/sessions/$sid.stage" + +case "${1:-check}" in + lock) + # 需要 stage 與 model 兩個參數,缺一就安靜降級 + if [ -n "${2:-}" ] && [ -n "${3:-}" ]; then + printf '%s %s\n' "$2" "$3" > "$state" 2>/dev/null || true + fi ;; + unlock) + rm -f "$state" 2>/dev/null || true ;; + check) + [ -f "$state" ] || exit 0 + stage=$(sed -n '1s/ .*//p' "$state" 2>/dev/null) + locked=$(sed -n '1s/^[^ ]* //p' "$state" 2>/dev/null) + [ -n "$stage" ] && [ -n "$locked" ] || exit 0 + # 目前模型判定順序:stdin JSON model > JSC_MODEL > ~/.claude/settings.json(僅 JSC_CLI 為 claude 或未設定時) + cur=$(json_str model) + [ -n "$cur" ] || cur="${JSC_MODEL:-}" + if [ -z "$cur" ] && { [ -z "${JSC_CLI:-}" ] || [ "${JSC_CLI:-}" = "claude" ]; }; then + cur=$(tr -d '\n' < "$HOME/.claude/settings.json" 2>/dev/null \ + | sed -n 's/.*"model"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n1) + fi + [ -n "$cur" ] || exit 0 + # 別名相容:一方包含另一方就視為同一模型(例:opus 對 claude-opus-4-6) + case "$cur" in *"$locked"*) exit 0 ;; esac + case "$locked" in *"$cur"*) exit 0 ;; esac + echo "[jsc] SDLC 模型鎖:階段「${stage}」已鎖定模型「${locked}」,目前模型「${cur}」不符。請拒絕執行任何 SDLC 工作,並請使用者切回模型「${locked}」後再繼續。此鎖會在下一個 SDLC 階段閘門執行時解除。" ;; + report) + if [ -f "$state" ]; then + line=$(sed -n '1p' "$state" 2>/dev/null) + [ -n "$line" ] && echo "$sid $line" + fi ;; +esac +exit 0 From bb91cc2832c96956251a1a8294d6fc4487de9383 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 24 Aug 2026 11:36:24 +0800 Subject: [PATCH 02/10] =?UTF-8?q?feat(wrapper):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E7=84=A1=20Hook=20CLI=20=E7=9A=84=E5=8C=85=E8=A3=9D=E5=95=9F?= =?UTF-8?q?=E5=8B=95=E5=99=A8=E8=88=87=E6=97=A5=E8=AA=8C=E5=9B=9E=E5=A1=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:新增 tools/jsc-wrap.sh 包裝啟動器與 tools/scan-logs.sh 離線日誌回填腳本。 Why:copilot、antigravity 沒有 hook 系統,計時與技能用量原本完全缺漏,需要 hook 之外的落地方案。 How:jsc-wrap.sh 匯出 JSC_CLI 與 JSC_SESSION_ID,啟動前後各呼叫一次 session-timer.sh,結束時自動執行 scan-logs.sh 並原樣傳遞子行程退出碼;scan-logs.sh 解析 copilot、antigravity、codex 的原生日誌,把技能用量與階段界線補進 $JSC_HOME,重掃不產生重複記錄。 Who:無 hook 系統的 CLI(copilot、antigravity)與僅有 notify 的 codex,資料供 jsc-log:worklog 與 jsc-log:stats 使用。 Co-Authored-By: Claude Fable 5 --- tools/jsc-wrap.sh | 24 ++++++++++++ tools/scan-logs.sh | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100755 tools/jsc-wrap.sh create mode 100755 tools/scan-logs.sh diff --git a/tools/jsc-wrap.sh b/tools/jsc-wrap.sh new file mode 100755 index 0000000..88bcf6b --- /dev/null +++ b/tools/jsc-wrap.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env sh +# jsc-wrap.sh — 無 hook 系統 CLI 的包裝啟動器(例:copilot、antigravity)。 +# 用法: jsc-wrap.sh {cli} [args...] +# 行為: 匯出 JSC_CLI 與 JSC_SESSION_ID → session-timer start → 執行 CLI → +# 結束後 session-timer mark 並以 scan-logs.sh 回填用量,最後回傳 CLI 的結束碼。 +HERE=$(cd "$(dirname "$0")" && pwd) +HOOKS="$HERE/../hooks" +cli="${1:-}" +if [ -z "$cli" ]; then + echo "用法:jsc-wrap.sh {cli} [args...]" >&2 + exit 2 +fi +shift +JSC_CLI="$cli" +# 未提供 session id 就自動產生({cli}-時間戳-PID),讓計時與用量共用同一個 session +[ -n "${JSC_SESSION_ID:-}" ] || JSC_SESSION_ID="$cli-$(date +%Y%m%d%H%M%S)-$$" +export JSC_CLI JSC_SESSION_ID +sh "$HOOKS/session-timer.sh" start &2 + exit 2 +fi + +STATE="$JSC_HOME/usage/scan-state" +mkdir -p "$STATE" 2>/dev/null || true + +# 檔案修改時間(epoch),取不到就用現在 +mtime_epoch() { + stat -c %Y "$1" 2>/dev/null || date -r "$1" +%s 2>/dev/null || now_epoch +} + +# 回填工作階段界線:start 只補缺,end 取較新值(不覆蓋較新的即時紀錄) +record_session() { # $1=sid $2=epoch + [ -f "$JSC_HOME/sessions/$1.start" ] || echo "$2" > "$JSC_HOME/sessions/$1.start" + old=$(cat "$JSC_HOME/sessions/$1.end" 2>/dev/null || echo 0) + if [ "$2" -gt "$old" ] 2>/dev/null; then echo "$2" > "$JSC_HOME/sessions/$1.end"; fi +} + +# 印出日誌檔自上次掃描後的新增內容,並更新位移(位移即去重機制) +new_content() { # $1=file + key=$(printf '%s' "$1" | cksum | tr ' \t' '--') + off_f="$STATE/$cli-$key.offset" + off=$(cat "$off_f" 2>/dev/null || echo 0) + size=$(wc -c < "$1" 2>/dev/null || echo 0) + [ "$size" -gt "$off" ] 2>/dev/null || return 0 + tail -c +"$((off + 1))" "$1" 2>/dev/null + echo "$size" > "$off_f" +} + +# 從新增內容萃取 jsc 技能代號,逐筆追加(同一批先去重;格式與 skill-usage.sh 完全相同) +append_skills() { # stdin=內容 $1=sid + grep -oE 'jsc-[a-z]+:[a-z0-9][a-z0-9-]*' 2>/dev/null | sort -u | while IFS= read -r sk; do + printf '{"ts":"%s","cli":"%s","session":"%s","skill":"%s"}\n' \ + "$(now_iso)" "$cli" "$1" "$sk" >> "$JSC_HOME/usage/skills.jsonl" + done +} + +# 掃描一個日誌目錄;$2=預設 sid(空字串時用檔名當 sid);$3=sessions 時回填階段界線 +scan_dir() { + find "$1" -maxdepth 3 -type f \( -name '*.json' -o -name '*.jsonl' -o -name '*.log' \) 2>/dev/null \ + | while IFS= read -r f; do + s="$2" + [ -n "$s" ] || s="$cli-$(basename "$f" | sed 's/\.[^.]*$//')" + [ "${3:-}" = "sessions" ] && record_session "$s" "$(mtime_epoch "$f")" + new_content "$f" | append_skills "$s" + done + return 0 +} + +case "$cli" in + copilot) + d="$HOME/.config/copilot" + if [ ! -d "$d" ]; then echo "[jsc] 找不到 copilot 日誌目錄($d),略過回填。"; exit 0; fi + [ -d "$d/history-session-state" ] && scan_dir "$d/history-session-state" "$sid" sessions + [ -d "$d/history" ] && scan_dir "$d/history" "$sid" sessions + [ -d "$d/logs" ] && scan_dir "$d/logs" "$sid" logs ;; + antigravity) + found="" + for d in "$HOME/.antigravity/logs" "$HOME/.config/antigravity/logs" "$HOME/.antigravity"; do + if [ -d "$d" ]; then found=1; scan_dir "$d" "$sid" sessions; break; fi + done + if [ -z "$found" ]; then echo "[jsc] 找不到 antigravity 日誌目錄,略過回填。"; exit 0; fi ;; + codex) + d1="$HOME/.codex/sessions"; d2="$HOME/.codex/log" + if [ ! -d "$d1" ] && [ ! -d "$d2" ]; then + echo "[jsc] 找不到 codex 日誌目錄($d1、$d2),略過回填。"; exit 0 + fi + [ -d "$d1" ] && scan_dir "$d1" "$sid" sessions + [ -d "$d2" ] && scan_dir "$d2" "$sid" logs ;; + *) + echo "[jsc] 尚未支援 $cli 的日誌回填,略過。" ;; +esac +exit 0 From 8a95bd2b47324b8ba7b1accba0d67ee4d59a6f6e Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 24 Aug 2026 11:36:40 +0800 Subject: [PATCH 03/10] =?UTF-8?q?feat(ste100):=20=E5=8A=A0=E5=85=A5?= =?UTF-8?q?=E9=80=81=E5=87=BA=E5=89=8D=E8=87=AA=E6=88=91=E6=AA=A2=E6=9F=A5?= =?UTF-8?q?=E6=B8=85=E5=96=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:ste100-guard.sh 追加一行輸出,列出五項送出前自我檢查(簡體字、半形句尾標點、套路句、半形斜線並列、諂媚開場),並附常見例字。 Why:只宣告規則不足以攔下高頻違規,參考 oh-my-pi 的做法,把可自查的具體症狀直接列給模型,命中就先改再送出。 How:在既有規則宣告後追加第二個 echo,維持單行文字、exit 0,不影響任何 CLI 的接線方式。 Who:所有經 UserPromptSubmit hook 或規則檔取得 STE100 規則的 AI CLI 輸出。 Co-Authored-By: Claude Fable 5 --- hooks/ste100-guard.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/hooks/ste100-guard.sh b/hooks/ste100-guard.sh index 6746cf4..171d4ee 100755 --- a/hooks/ste100-guard.sh +++ b/hooks/ste100-guard.sh @@ -4,4 +4,5 @@ # 其他 CLI: 由 hooks-install 以各自的規則檔(AGENTS.md 等)落地,本腳本仍可被 wrapper 呼叫。 # 完整規則的唯一來源:jsc-meta 的 references/ste100.md。 echo "[jsc] 輸出規則:STE100 繁體中文,擬人台灣感。短句、一句一指令、主動語態、術語一致;台灣用語(預設、支援、相容、資訊);全形標點;去 AI 味(不用「總的來說」「首先/其次/最後」開場收尾套路、不諂媚);直接講重點,UTF-8 無亂碼。完整規則見 jsc-meta 的 references/ste100.md。" +echo "[jsc] 送出前自我檢查,命中任一項就先改再送出:1)簡體字(例:应、为、这、说、后、发);2)句尾用半形標點(. , ! ?),應為全形(。,!?);3)套路句(「總的來說」「綜上所述」「首先…其次…最後」);4)中文並列用半形「/」,應改頓號「、」;5)諂媚開場(「好問題」「當然可以」)。" exit 0 From bdfb4619af64845712652ebc1d803263a6f31062 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 24 Aug 2026 11:36:54 +0800 Subject: [PATCH 04/10] =?UTF-8?q?docs(hooks-install):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=9B=9B=E6=94=AF=20hook=20=E7=9A=84=E6=8E=A5=E7=B7=9A?= =?UTF-8?q?=E8=AA=AA=E6=98=8E=E8=88=87=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:hooks-install SKILL.md 改寫接線表並加上逐 CLI 驗證欄;README 補上 sdlc-gate、tools 兩支腳本與 JSC_MODEL 變數說明;AGENTS.md 同步 domain 描述。 Why:新增 SDLC 模型鎖與 wrapper 方案後,安裝流程與環境變數契約已改變,文件必須與實作一致,否則接線技能會漏裝。 How:接線表改為三欄(CLI、接線、驗證),copilot 與 antigravity 改兩段式(jsc-wrap.sh 包裝+規則檔追加),codex 與 kiro 註明模型鎖降級為技能步驟檢查;每步補上完成判準。 Who:執行 hooks-install 技能的 AI 助理與維護 jsc-hooks domain 的開發者。 Co-Authored-By: Claude Fable 5 --- AGENTS.md | 2 +- README.md | 15 ++++++++++++--- skills/hooks-install/SKILL.md | 29 +++++++++++++++-------------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 89f890e..5196855 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # jsc-hooks — 給 AI 助理的指引 -本 repo 是 jsc 技能組的 `hooks` domain(跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄),可同時被 Claude Code / Codex / Copilot / Antigravity / Kiro 使用。 +本 repo 是 jsc 技能組的 `hooks` domain(跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖),可同時被 Claude Code / Codex / Copilot / Antigravity / Kiro 使用。 ## 規則 diff --git a/README.md b/README.md index 729910f..1c65465 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,16 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/jsc.git),安 | `hooks/ste100-guard.sh` | UserPromptSubmit | 注入 STE100 繁體中文輸出規則(hook > prompt 強制層) | | `hooks/session-timer.sh` | SessionStart / Stop / SessionEnd | 記錄工作階段起訖;`report` 子指令供 `jsc-log:worklog` 取花費時間 | | `hooks/skill-usage.sh` | PostToolUse(Skill) | 記錄技能使用與呼叫鏈到 `$JSC_HOME/usage/*.jsonl`,供 `jsc-log:stats` 統計 | +| `hooks/sdlc-gate.sh` | UserPromptSubmit | SDLC 每 session 模型鎖:`lock`/`unlock` 由 jsc-sdlc 階段技能呼叫,`check` 在模型與鎖不符時注入拒絕提醒 | -Claude 由 `hooks/hooks.json` 自動接線;其他 CLI 用 `hooks-install` 技能接線或降級為規則檔。 +Claude 由 `hooks/hooks.json` 自動接線;其他 CLI 用 `hooks-install` 技能接線、改裝包裝啟動器,或降級為規則檔。 + +## 工具 + +| 腳本 | 用途 | +| --- | --- | +| `tools/jsc-wrap.sh` | 無 hook 系統 CLI 的包裝啟動器:匯出 `JSC_CLI`、`JSC_SESSION_ID`,前後接 `session-timer.sh`,結束時自動跑 `scan-logs.sh` 回填 | +| `tools/scan-logs.sh` | 離線回填:解析 copilot、antigravity、codex 的原生日誌,把技能用量與階段界線補進 `$JSC_HOME`,重掃不重複 | ## 失敗回報範本 @@ -44,7 +52,7 @@ Claude 由 `hooks/hooks.json` 自動接線;其他 CLI 用 `hooks-install` 技 ### `hooks-install` -把三支 hook 接線到所有已安裝的 CLI:偵測 CLI 後逐一套用原生 hook 設定,無 hook 系統的 CLI 降級為規則檔(以 `` 標記整段取代,不重複追加)。 +把四支 hook 接線到所有已安裝的 CLI:偵測 CLI 後逐一套用原生 hook 設定(claude 由 `hooks.json` 自動接線)。copilot、antigravity 改用兩段式:以 `tools/jsc-wrap.sh` 包裝啟動器補上計時與用量回填(結束時自動跑 `tools/scan-logs.sh`),語言規則仍追加到各自的規則檔(以 `` 標記整段取代,不重複追加)。codex、kiro 的 SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 SDLC 技能直接呼叫 `sdlc-gate.sh lock` 寫入。 @@ -53,7 +61,8 @@ Claude 由 `hooks/hooks.json` 自動接線;其他 CLI 用 `hooks-install` 技 | 變數 | 用途 | 未設定時 | | --- | --- | --- | | `JSC_HOME` | Hook 資料目錄 | 預設 `~/.jsc` | -| `JSC_CLI` / `JSC_SESSION_ID` / `JSC_SKILL` | 非 Claude CLI 接線時由 wrapper 設定 | 安靜降級 | +| `JSC_CLI` / `JSC_SESSION_ID` / `JSC_SKILL` | 非 Claude CLI 接線時由 `tools/jsc-wrap.sh` 或接線設定提供 | 安靜降級 | +| `JSC_MODEL` | 非 Claude CLI 的目前模型,供 `sdlc-gate.sh check` 比對 | 改讀 `~/.claude/settings.json`,再不行就安靜降級 | ## 相關 domain diff --git a/skills/hooks-install/SKILL.md b/skills/hooks-install/SKILL.md index 4c51e44..867c443 100644 --- a/skills/hooks-install/SKILL.md +++ b/skills/hooks-install/SKILL.md @@ -1,30 +1,31 @@ --- name: hooks-install -description: Wire jsc hooks (STE100 guard, session timer, skill usage logger) into every installed AI CLI. Detect CLIs via jsc-cli detect-clis.sh, then apply native hook config per CLI or fall back to instruction files. Use after installing or updating the jsc plugin set; not for writing new hooks. +description: Wire jsc hooks (STE100 guard, session timer, skill usage logger, SDLC model gate) into every installed AI CLI. Detect CLIs via jsc-cli detect-clis.sh, then apply native hook config, the jsc-wrap.sh launcher, or an instruction-file fallback per CLI. Use after installing or updating the jsc plugin set; not for writing new hooks. --- # hooks-install — wire jsc hooks into every installed CLI -Goal: make the three hooks (`ste100-guard.sh`, `session-timer.sh`, `skill-usage.sh`) effective in every CLI. +Goal: make the four hooks (`ste100-guard.sh`, `session-timer.sh`, `skill-usage.sh`, `sdlc-gate.sh`) effective in every CLI. +Claude wiring is automatic via `hooks.json`. On codex and kiro the SDLC gate degrades to the skill-step check only; the lock file still works because the SDLC skills call `sdlc-gate.sh lock` directly. The detailed flow **MUST run as a sub agent**; the main agent only reports the summary. ## Steps -1. Run `jsc-cli/tools/detect-clis.sh` to get the installed CLIs and their paths. -2. Wire each CLI per the table below (hooks first; degrade to an instruction file when the CLI has no hook system): +1. Run `jsc-cli/tools/detect-clis.sh`. Done when you hold the list of installed CLIs; when the list is empty, report that and stop. +2. Wire each CLI per the table below. A row counts as done only when its verify check passes: -| CLI | Wiring | -| --- | --- | -| claude | the plugin loads `jsc-hooks/hooks/hooks.json` automatically on install; verify only: `claude plugin list` shows `jsc-hooks` and `/hooks` shows the registrations | -| codex | set `notify` in `~/.codex/config.toml` to `session-timer.sh mark` (with `JSC_CLI=codex`); append the STE100 rule to `~/.codex/AGENTS.md` (prompt fallback) | -| copilot | no hook system: append the STE100 rule to `~/.config/copilot/copilot-instructions.md` (prompt fallback) | -| antigravity | no public hook system: append the STE100 rule to the global rules file (prompt fallback) | -| kiro | create an agent hook under the workspace `.kiro/hooks/` that runs the matching script (with `JSC_CLI=kiro`) | +| CLI | Wiring | Verify | +| --- | --- | --- | +| claude | the plugin loads `jsc-hooks/hooks/hooks.json` automatically on install (all four hooks, `sdlc-gate.sh check` included); nothing to write | `claude plugin list` shows `jsc-hooks` and `/hooks` shows the registrations | +| codex | set `notify` in `~/.codex/config.toml` to `session-timer.sh mark` (with `JSC_CLI=codex`); append the STE100 rule block to `~/.codex/AGENTS.md` (prompt fallback) | `config.toml` contains the `notify` entry and `AGENTS.md` contains the block | +| copilot | (a) install the wrapper: add a shell alias or launcher that starts the CLI via `jsc-hooks/tools/jsc-wrap.sh copilot`, so session timing and usage backfill work — `tools/scan-logs.sh` runs automatically at wrapper exit; (b) keep appending the STE100 rule block to `~/.config/copilot/copilot-instructions.md` (prompt fallback for the language rules) | the alias resolves to `jsc-wrap.sh copilot` and the instruction file contains the block | +| antigravity | (a) install the wrapper: add a shell alias or launcher that starts the CLI via `jsc-hooks/tools/jsc-wrap.sh antigravity` — `tools/scan-logs.sh` runs automatically at wrapper exit; (b) keep appending the STE100 rule block to the global rules file (prompt fallback for the language rules) | the alias resolves to `jsc-wrap.sh antigravity` and the rules file contains the block | +| kiro | create an agent hook under the workspace `.kiro/hooks/` that runs the matching script (with `JSC_CLI=kiro`) | the hook file exists under `.kiro/hooks/` and names the script | -3. The appended rule text is always the exact output of `ste100-guard.sh` (a Traditional Chinese literal). Wrap it in `` markers; on re-run, replace the whole block instead of appending again. -4. Report the wiring result for every CLI: succeeded, degraded to prompt, or the skip reason. +3. The appended rule text is always the exact output of `ste100-guard.sh` (a Traditional Chinese literal). Wrap it in `` markers; on re-run, replace the whole block instead of appending again. Done when each touched instruction file contains exactly one `` block. +4. Report the wiring result for every CLI. Done when every detected CLI has exactly one status: succeeded, degraded to prompt, or skipped with a reason. ## Notes -- The hook scripts accept both stdin JSON and environment variables (`JSC_CLI`, `JSC_SESSION_ID`, `JSC_SKILL`); a wrapper only needs to set the variables. +- The hook scripts accept both stdin JSON and environment variables (`JSC_CLI`, `JSC_SESSION_ID`, `JSC_SKILL`, `JSC_MODEL`); `jsc-wrap.sh` sets the first two itself. - Data lands in `$JSC_HOME` (default `~/.jsc`), consumed by `jsc-log:worklog` and `jsc-log:stats`. From aae0516cb5a01f8f833353a3b7fa5d832a23421e Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 24 Aug 2026 11:37:08 +0800 Subject: [PATCH 05/10] =?UTF-8?q?chore(manifest):=20=E4=B8=89=E4=BB=BD=20m?= =?UTF-8?q?anifest=20=E5=8D=87=E7=89=88=E8=87=B3=200.0.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What:plugin.json、.claude-plugin/plugin.json、.codex-plugin/plugin.json 版本由 0.0.2 升至 0.0.3,描述補上「SDLC 模型鎖」。 Why:本次新增 SDLC 模型鎖 hook 與 wrapper 工具屬功能擴充,需升版讓各 CLI 的 plugin 更新機制抓到新版本,描述也要反映新能力。 How:三份 manifest 同步改 version 與 description,維持欄位結構不變。 Who:透過 jsc marketplace 安裝 jsc-hooks 的所有 AI CLI 與 jsc-cli:deploy 佈署流程。 Co-Authored-By: Claude Fable 5 --- .claude-plugin/plugin.json | 4 ++-- .codex-plugin/plugin.json | 4 ++-- plugin.json | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index e6f1c9c..f37c133 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "jsc-hooks", - "version": "0.0.2", - "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄", + "version": "0.0.3", + "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖", "skills": "./skills", "author": { "name": "JSC" diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index ab9fbcb..6634e98 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.0.2", - "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄", + "version": "0.0.3", + "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖", "skills": "./skills" } diff --git a/plugin.json b/plugin.json index ecc0924..62b874d 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.0.2", - "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄", + "version": "0.0.3", + "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖", "skills": "./skills/" } From c4909e1a1ff869cf8d97e60104bcc0971c045294 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 24 Aug 2026 14:47:36 +0800 Subject: [PATCH 06/10] =?UTF-8?q?fix(marketplace):=20=E4=BF=AE=E6=AD=A3=20?= =?UTF-8?q?marketplace.json=20=E8=88=87=E6=AD=A3=E5=85=B8=E8=A8=BB?= =?UTF-8?q?=E5=86=8A=E8=A1=A8=E4=B8=8D=E5=90=8C=E6=AD=A5=E7=9A=84=E6=AC=84?= =?UTF-8?q?=E4=BD=8D=E7=BC=BA=E6=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 為什麼: `.claude-plugin/marketplace.json` 與 `.agents/plugins/marketplace.json` 兩份檔案內容與 `plugins/jsc` 正典 marketplace 註冊表不一致,導致各 domain repo 的 marketplace 副本無法保持逐位元組同步。 做了什麼: 補上 `.agents/plugins/marketplace.json` 缺漏的頂層 `description`、`owner` 物件的 `description`,以及全部 11 個外掛(含 jsc-hooks 本身)缺漏的 per-plugin `description` 欄位;同時修正 `.claude-plugin/marketplace.json` 中 jsc-meta 描述的標點不一致問題,將斜線改為中文頓號,與正典風格一致。 怎麼做: 逐欄位比對正典 `plugins/jsc/marketplace.json` 內容,補齊並修正兩份檔案至與正典完全一致。 影響範圍: `.claude-plugin/marketplace.json`、`.agents/plugins/marketplace.json`,屬全 repo 範圍的基礎設施修正,不影響任何 skill 邏輯或執行行為。 --- .agents/plugins/marketplace.json | 34 ++++++++++++++++++++++---------- .claude-plugin/marketplace.json | 2 +- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 12df680..a9b4e66 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -1,75 +1,89 @@ { "name": "jsc", + "description": "jsc 跨 AI 助理技能組的統一 marketplace(claude / codex / copilot / antigravity / kiro)。", + "owner": { + "name": "JSC" + }, "plugins": [ { "name": "jsc-ask", "source": { "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/ask.git" - } + }, + "description": "決策樹問詢與問詢紀錄(QUESTION_* wiki 頁)" }, { "name": "jsc-cli", "source": { "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/cli.git" - } + }, + "description": "CLI 偵測、模型能力標籤與技能庫批次部署" }, { "name": "jsc-git", "source": { "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/git.git" - } + }, + "description": "Commit 分組認可與 Push Request 建立" }, { "name": "jsc-gitea", "source": { "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/gitea.git" - } + }, + "description": "Gitea API 工具、Wiki 讀寫與存取庫批次同步" }, { "name": "jsc-hooks", "source": { "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/hooks.git" - } + }, + "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄" }, { "name": "jsc-log", "source": { "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/log.git" - } + }, + "description": "工作日誌(LOG_* wiki 頁)與技能使用統計" }, { "name": "jsc-meta", "source": { "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/meta.git" - } + }, + "description": "技能組自我管理:新建、更新、刪除技能與技能準則" }, { "name": "jsc-pkg", "source": { "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/pkg.git" - } + }, + "description": "套件批次更新(nodejs/python/dotnet),失敗還原" }, { "name": "jsc-review", "source": { "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/review.git" - } + }, + "description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組" }, { "name": "jsc-sdlc", "source": { "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/sdlc.git" - } + }, + "description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)" } ] } diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 28bb223..a9b4e66 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -59,7 +59,7 @@ "source": "url", "url": "https://gitea.jsc.idv.tw/plugins/meta.git" }, - "description": "技能組自我管理:新建/更新/刪除技能與技能準則" + "description": "技能組自我管理:新建、更新、刪除技能與技能準則" }, { "name": "jsc-pkg", From 69eee91f27aae8453048ffe14d1565d78cb9decc Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 24 Aug 2026 14:47:46 +0800 Subject: [PATCH 07/10] =?UTF-8?q?feat(hooks-install):=20=E5=B0=87=20hooks-?= =?UTF-8?q?install=20=E7=9A=84=20CLI=20=E4=BD=88=E7=B7=9A=E9=82=8F?= =?UTF-8?q?=E8=BC=AF=E6=8A=BD=E9=9B=A2=E7=82=BA=E8=85=B3=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 為什麼: `skills/hooks-install/SKILL.md` 原本直接內嵌各 CLI(codex/copilot/antigravity/kiro)的詳細佈線步驟,包含逐步設定檔修改、wrapper alias 安裝、以及 `` 標記區塊的冪等替換邏輯,違反 jsc-meta:skill-check 稽核準則中「確定性程序邏輯應由腳本執行,不應留給子代理每次自行解讀 prose」的要求。 做了什麼: 新增腳本 `tools/wire-cli.sh {cli}`,將原本內嵌於 SKILL.md 的佈線邏輯全數抽出,並支援冪等執行(標記區塊替換、可安全重跑),輸出機器可讀的 `status=wired|degraded|skipped reason=...` 首行訊息與對應結束碼(0=wired、1=degraded、2=bad usage、3=skipped)。改寫 `skills/hooks-install/SKILL.md`,改為依偵測到的 CLI 呼叫此腳本,並讀取其 status/結束碼判斷結果,不再自行內含佈線步驟。同步更新 `README.md`:在工具表新增 `tools/wire-cli.sh` 說明列,並改寫 hooks-install skill 的描述段落,反映新的腳本驅動流程與 `status=` 回報慣例。三份外掛清單 `plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json` 版本號自 0.0.3 升級為 0.0.4,對應此行為變更。 怎麼做: 建立獨立腳本承載原本的 CLI 佈線程序邏輯,改造 SKILL.md 為呼叫腳本並解讀其標準化輸出,同步更新文件與版本號。 影響範圍: `skills/hooks-install/SKILL.md`、`README.md`、`plugin.json`、`.claude-plugin/plugin.json`、`.codex-plugin/plugin.json`、新增檔案 `tools/wire-cli.sh`;影響 hooks-install skill 的執行流程與所有安裝 jsc-hooks 外掛的 CLI 佈線行為。 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- README.md | 3 +- plugin.json | 2 +- skills/hooks-install/SKILL.md | 18 ++-- tools/wire-cli.sh | 149 ++++++++++++++++++++++++++++++++++ 6 files changed, 163 insertions(+), 13 deletions(-) create mode 100755 tools/wire-cli.sh diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index f37c133..dcbb8d3 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.0.3", + "version": "0.0.4", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 6634e98..8d576b3 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.0.3", + "version": "0.0.4", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖", "skills": "./skills" } diff --git a/README.md b/README.md index 1c65465..efa1b56 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Claude 由 `hooks/hooks.json` 自動接線;其他 CLI 用 `hooks-install` 技 | --- | --- | | `tools/jsc-wrap.sh` | 無 hook 系統 CLI 的包裝啟動器:匯出 `JSC_CLI`、`JSC_SESSION_ID`,前後接 `session-timer.sh`,結束時自動跑 `scan-logs.sh` 回填 | | `tools/scan-logs.sh` | 離線回填:解析 copilot、antigravity、codex 的原生日誌,把技能用量與階段界線補進 `$JSC_HOME`,重掃不重複 | +| `tools/wire-cli.sh` | 單一 CLI 的接線流程:`{cli}` 對應的設定編輯、包裝別名安裝、hook 檔建立,皆以 ``(或 `# jsc-hooks`)標記整段取代,重跑不重複;以 `status=wired\|degraded\|skipped` 回報結果 | ## 失敗回報範本 @@ -52,7 +53,7 @@ Claude 由 `hooks/hooks.json` 自動接線;其他 CLI 用 `hooks-install` 技 ### `hooks-install` -把四支 hook 接線到所有已安裝的 CLI:偵測 CLI 後逐一套用原生 hook 設定(claude 由 `hooks.json` 自動接線)。copilot、antigravity 改用兩段式:以 `tools/jsc-wrap.sh` 包裝啟動器補上計時與用量回填(結束時自動跑 `tools/scan-logs.sh`),語言規則仍追加到各自的規則檔(以 `` 標記整段取代,不重複追加)。codex、kiro 的 SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 SDLC 技能直接呼叫 `sdlc-gate.sh lock` 寫入。 +把四支 hook 接線到所有已安裝的 CLI:偵測 CLI 後,逐一呼叫 `tools/wire-cli.sh {cli}` 完成接線(claude 由 `hooks.json` 自動接線,無需寫入)。copilot、antigravity 由該腳本裝上 `tools/jsc-wrap.sh` 包裝別名補上計時與用量回填(結束時自動跑 `tools/scan-logs.sh`),語言規則仍追加到各自的規則檔(以 `` 標記整段取代,不重複追加)。codex、kiro 的 SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 SDLC 技能直接呼叫 `sdlc-gate.sh lock` 寫入。腳本以 `status=wired|degraded|skipped` 回報結果,供技能對照 verify 表。 diff --git a/plugin.json b/plugin.json index 62b874d..9179cb9 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.0.3", + "version": "0.0.4", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖", "skills": "./skills/" } diff --git a/skills/hooks-install/SKILL.md b/skills/hooks-install/SKILL.md index 867c443..0de658f 100644 --- a/skills/hooks-install/SKILL.md +++ b/skills/hooks-install/SKILL.md @@ -12,18 +12,18 @@ The detailed flow **MUST run as a sub agent**; the main agent only reports the s ## Steps 1. Run `jsc-cli/tools/detect-clis.sh`. Done when you hold the list of installed CLIs; when the list is empty, report that and stop. -2. Wire each CLI per the table below. A row counts as done only when its verify check passes: +2. For each installed CLI, run `tools/wire-cli.sh {cli}`. The script performs the config edit, wrapper alias install, or hook file creation for that CLI, and replaces its `` (or `# jsc-hooks`) marker block idempotently — reruns never duplicate content. Read its exit code and first output line (`status=wired|degraded|skipped reason=...`), then confirm against the table below: -| CLI | Wiring | Verify | +| CLI | Exit / status | Verify | | --- | --- | --- | -| claude | the plugin loads `jsc-hooks/hooks/hooks.json` automatically on install (all four hooks, `sdlc-gate.sh check` included); nothing to write | `claude plugin list` shows `jsc-hooks` and `/hooks` shows the registrations | -| codex | set `notify` in `~/.codex/config.toml` to `session-timer.sh mark` (with `JSC_CLI=codex`); append the STE100 rule block to `~/.codex/AGENTS.md` (prompt fallback) | `config.toml` contains the `notify` entry and `AGENTS.md` contains the block | -| copilot | (a) install the wrapper: add a shell alias or launcher that starts the CLI via `jsc-hooks/tools/jsc-wrap.sh copilot`, so session timing and usage backfill work — `tools/scan-logs.sh` runs automatically at wrapper exit; (b) keep appending the STE100 rule block to `~/.config/copilot/copilot-instructions.md` (prompt fallback for the language rules) | the alias resolves to `jsc-wrap.sh copilot` and the instruction file contains the block | -| antigravity | (a) install the wrapper: add a shell alias or launcher that starts the CLI via `jsc-hooks/tools/jsc-wrap.sh antigravity` — `tools/scan-logs.sh` runs automatically at wrapper exit; (b) keep appending the STE100 rule block to the global rules file (prompt fallback for the language rules) | the alias resolves to `jsc-wrap.sh antigravity` and the rules file contains the block | -| kiro | create an agent hook under the workspace `.kiro/hooks/` that runs the matching script (with `JSC_CLI=kiro`) | the hook file exists under `.kiro/hooks/` and names the script | +| claude | `status=wired` (exit 0) — hooks.json auto-wires everything, nothing to write | `claude plugin list` shows `jsc-hooks` and `/hooks` shows the registrations | +| codex | `status=degraded` (exit 1) — notify + AGENTS.md prompt fallback | `~/.codex/config.toml` contains the `notify` entry and `AGENTS.md` contains the block | +| copilot | `status=wired` (exit 0) when the CLI is detected, `status=skipped` (exit 3) otherwise | the alias resolves to `jsc-wrap.sh copilot` and `copilot-instructions.md` contains the block | +| antigravity | `status=wired` (exit 0) when the CLI is detected, `status=skipped` (exit 3) otherwise | the alias resolves to `jsc-wrap.sh antigravity` and the rules file contains the block | +| kiro | `status=degraded` (exit 1) | the hook file exists under `.kiro/hooks/` and names the script | -3. The appended rule text is always the exact output of `ste100-guard.sh` (a Traditional Chinese literal). Wrap it in `` markers; on re-run, replace the whole block instead of appending again. Done when each touched instruction file contains exactly one `` block. -4. Report the wiring result for every CLI. Done when every detected CLI has exactly one status: succeeded, degraded to prompt, or skipped with a reason. +A row counts as done only when its verify check passes. Exit 2 means bad usage (wrong CLI name), not a wiring outcome. +3. Report the exact `status=` line `tools/wire-cli.sh` printed for each CLI; do not reinterpret or recompute the outcome by hand. Done when every detected CLI has exactly one reported status: wired, degraded, or skipped with a reason. ## Notes diff --git a/tools/wire-cli.sh b/tools/wire-cli.sh new file mode 100755 index 0000000..22f9ae1 --- /dev/null +++ b/tools/wire-cli.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env sh +# wire-cli.sh — 把 jsc 四支 hook 接線到單一 CLI(供 hooks-install 技能呼叫)。 +# 用法: wire-cli.sh {claude|codex|copilot|antigravity|kiro} +# 行為(依 CLI 而定,皆為冪等:重跑只取代既有的 jsc-hooks 標記段落,不會重複疊加): +# claude — 什麼都不用寫,hooks.json 已自動接線四支 hook +# codex — 在 config.toml 設 notify(呼叫 session-timer.sh mark,JSC_CLI=codex); +# 在 AGENTS.md 附加 STE100 規則段落(prompt 降級) +# copilot — 在 shell rc 檔加上 copilot 別名,轉呼叫 tools/jsc-wrap.sh copilot; +# 在 copilot-instructions.md 附加 STE100 規則段落 +# antigravity — 在 shell rc 檔加上 agy 別名,轉呼叫 tools/jsc-wrap.sh antigravity; +# 在全域規則檔附加 STE100 規則段落 +# kiro — 在工作區 .kiro/hooks/ 下建立 jsc-hooks.json(JSC_CLI=kiro) +# +# 輸出: 第一行固定為 `status={wired|degraded|skipped} reason=...`(可供程式判讀), +# 其後為人類可讀的繁中說明。 +# 結束碼: 0=wired(已完整接線) 1=degraded(降級為 prompt/技能步驟檢查) +# 2=用法錯誤 3=skipped(該 CLI 未偵測到執行檔,略過) +set -u +HERE=$(cd "$(dirname "$0")" && pwd) +ROOT=$(cd "$HERE/.." && pwd) +HOOKS="$ROOT/hooks" + +cli="${1:-}" +case "$cli" in + claude|codex|copilot|antigravity|kiro) ;; + *) + echo "用法:wire-cli.sh {claude|codex|copilot|antigravity|kiro}" >&2 + exit 2 ;; +esac + +# 每個 CLI 對應的實際執行檔名稱(antigravity 的執行檔是 agy,其餘與 CLI 代號同名) +cli_bin() { + case "$1" in + antigravity) printf 'agy' ;; + kiro) printf 'kiro-cli' ;; + *) printf '%s' "$1" ;; + esac +} + +# STE100 規則段落的唯一來源:ste100-guard.sh 的實際輸出 +ste100_text() { sh "$HOOKS/ste100-guard.sh" 2>/dev/null | sed '/^exit /d'; } + +# 以標記整段取代(冪等);標記不存在就在檔尾新增;檔案不存在就建立。 +# $1=檔案 $2=開頭標記行 $3=結尾標記行 $4=標記之間要寫入的內容 +replace_block() { + file="$1"; bopen="$2"; bshut="$3"; content="$4" + dir=$(dirname "$file") + mkdir -p "$dir" 2>/dev/null || return 1 + touch "$file" 2>/dev/null || return 1 + block=$(printf '%s\n%s\n%s' "$bopen" "$content" "$bshut") + if grep -qF "$bopen" "$file" 2>/dev/null; then + awk -v bopen="$bopen" -v bshut="$bshut" -v block="$block" ' + $0==bopen { print block; skip=1; next } + $0==bshut { skip=0; next } + skip { next } + { print } + ' "$file" > "$file.jsc-tmp" 2>/dev/null && mv "$file.jsc-tmp" "$file" + else + printf '\n%s\n' "$block" >> "$file" + fi +} + +# 找出可寫入別名的 shell rc 檔;都不存在就以 ~/.bashrc 為預設(自動建立)。 +# 印出找到/建立的 rc 檔路徑,一行一個。 +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" +} + +skip() { # $1=reason + printf 'status=skipped reason=%s\n' "$1" + echo "[jsc] 略過:$1" + exit 3 +} + +case "$cli" in + claude) + printf 'status=wired reason=%s\n' "hooks.json 自動接線" + echo "[jsc] claude:由 hooks/hooks.json 自動接線全部四支 hook,無需寫入設定。" + exit 0 ;; + + codex) + command -v "$(cli_bin codex)" >/dev/null 2>&1 || skip "未偵測到 codex 執行檔" + CODEX_HOME="${CODEX_HOME:-$HOME/.codex}" + config="$CODEX_HOME/config.toml" + agents="$CODEX_HOME/AGENTS.md" + notify_line="notify = [\"env\", \"JSC_CLI=codex\", \"sh\", \"$HOOKS/session-timer.sh\", \"mark\"]" + replace_block "$config" "# jsc-hooks" "# /jsc-hooks" "$notify_line" \ + || skip "無法寫入 $config" + replace_block "$agents" "" "" "$(ste100_text)" \ + || skip "無法寫入 $agents" + printf 'status=degraded reason=%s\n' "STE100 降級為 prompt 檔,SDLC 模型鎖降級為技能步驟檢查" + echo "[jsc] codex:已設定 $config 的 notify 呼叫 session-timer.sh mark。" + echo "[jsc] codex:已在 $agents 寫入 STE100 規則段落(prompt 降級)。" + echo "[jsc] codex:SDLC 模型鎖降級為技能步驟檢查,鎖檔仍由 sdlc-gate.sh lock 寫入。" + exit 1 ;; + + copilot) + bin=$(cli_bin copilot) + command -v "$bin" >/dev/null 2>&1 || skip "未偵測到 copilot 執行檔" + instr="${JSC_COPILOT_INSTRUCTIONS:-$HOME/.config/copilot/copilot-instructions.md}" + alias_line="alias $bin='sh \"$HERE/jsc-wrap.sh\" copilot'" + rc_files | while IFS= read -r rc; do + replace_block "$rc" "# jsc-hooks:copilot" "# /jsc-hooks:copilot" "$alias_line" + done + replace_block "$instr" "" "" "$(ste100_text)" \ + || skip "無法寫入 $instr" + printf 'status=wired reason=%s\n' "wrapper 別名 + 規則檔已接線" + echo "[jsc] copilot:已在 shell rc 加上 $bin 別名,轉呼叫 tools/jsc-wrap.sh copilot。" + echo "[jsc] copilot:已在 $instr 寫入 STE100 規則段落。" + exit 0 ;; + + antigravity) + bin=$(cli_bin antigravity) + command -v "$bin" >/dev/null 2>&1 || skip "未偵測到 antigravity(agy)執行檔" + rules="${JSC_ANTIGRAVITY_RULES:-$HOME/.antigravity/AGENTS.md}" + alias_line="alias $bin='sh \"$HERE/jsc-wrap.sh\" antigravity'" + rc_files | while IFS= read -r rc; do + replace_block "$rc" "# jsc-hooks:antigravity" "# /jsc-hooks:antigravity" "$alias_line" + done + replace_block "$rules" "" "" "$(ste100_text)" \ + || skip "無法寫入 $rules" + printf 'status=wired reason=%s\n' "wrapper 別名 + 規則檔已接線" + echo "[jsc] antigravity:已在 shell rc 加上 $bin 別名,轉呼叫 tools/jsc-wrap.sh antigravity。" + echo "[jsc] antigravity:已在 $rules 寫入 STE100 規則段落。" + exit 0 ;; + + kiro) + command -v "$(cli_bin kiro)" >/dev/null 2>&1 || skip "未偵測到 kiro-cli 執行檔" + hookdir="./.kiro/hooks" + hookfile="$hookdir/jsc-hooks.json" + mkdir -p "$hookdir" 2>/dev/null || skip "無法建立 $hookdir" + cat > "$hookfile" 2>/dev/null < Date: Mon, 24 Aug 2026 16:05:18 +0800 Subject: [PATCH 08/10] =?UTF-8?q?feat(sdlc-gate):=20=E9=9A=8E=E6=AE=B5?= =?UTF-8?q?=E9=96=98=E9=96=80=E6=94=B9=E7=82=BA=E8=83=BD=E5=8A=9B=E6=A8=99?= =?UTF-8?q?=E7=B1=A4=E7=A8=8B=E5=BC=8F=E5=88=A4=E5=AE=9A=EF=BC=8C=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E4=B8=8D=E7=AC=A6=E5=8D=B3=E6=93=8B=E4=B8=8B=E8=A9=B2?= =?UTF-8?q?=E8=BC=AA=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) --- hooks/lib.sh | 18 +++- hooks/sdlc-gate.sh | 179 ++++++++++++++++++++++++++++------ skills/hooks-install/SKILL.md | 3 +- 3 files changed, 169 insertions(+), 31 deletions(-) diff --git a/hooks/lib.sh b/hooks/lib.sh index baa3468..89961d2 100755 --- a/hooks/lib.sh +++ b/hooks/lib.sh @@ -1,7 +1,9 @@ #!/usr/bin/env sh # lib.sh — jsc hooks 共用函式。所有 hook 腳本 source 此檔。 # 輸入相容:Claude 式 stdin JSON、或環境變數(codex/copilot/antigravity/kiro 接線時設定)。 -# 缺資料時安靜降級,hook 永遠 exit 0,不可中斷宿主 CLI。 +# 缺資料時安靜降級,hook 預設 exit 0,不可中斷宿主 CLI。 +# 唯一例外:sdlc-gate.sh check 在「SDLC 階段鎖存在且模型不符」時會 exit 2 擋下該輪提示; +# 其餘情況(無鎖、資料不足無法判定)仍照舊 exit 0。 JSC_HOME="${JSC_HOME:-$HOME/.jsc}" mkdir -p "$JSC_HOME/sessions" "$JSC_HOME/usage" 2>/dev/null || true @@ -24,6 +26,20 @@ session_id() { printf '%s' "$sid" } +# 目前實際使用的模型 id:讀 transcript 最後一筆帶 model 的訊息。 +# 這是 shell 唯一能「驗證」的模型來源——模型自我回報無法驗證,等同沒有閘門。 +# 取不到(無 transcript_path、檔案不存在、尚無 assistant 訊息)時不輸出,由呼叫端決定如何降級。 +transcript_model() { + tp=$(json_str transcript_path) + [ -n "$tp" ] && [ -f "$tp" ] || return 0 + # 只掃尾端若干行即可命中最近一輪;`<...>` 這類佔位模型名(例如 )排除。 + tail -n 500 "$tp" 2>/dev/null \ + | grep -o '"model":"[^"]*"' \ + | sed 's/^"model":"//; s/"$//' \ + | grep -v '^<' \ + | tail -n 1 +} + # 目前 CLI 名稱:環境變數 > 依 stdin 特徵猜測 > unknown cli_name() { if [ -n "${JSC_CLI:-}" ]; then printf '%s' "$JSC_CLI" diff --git a/hooks/sdlc-gate.sh b/hooks/sdlc-gate.sh index cb1860f..1ca02ea 100755 --- a/hooks/sdlc-gate.sh +++ b/hooks/sdlc-gate.sh @@ -1,46 +1,167 @@ #!/usr/bin/env sh -# sdlc-gate.sh — SDLC 每 session 模型鎖(stage ∈ plan/analyze/implement/maintain)。 -# 狀態檔: $JSC_HOME/sessions/{sid}.stage,單行「{stage} {model}」。 -# 用法: -# sdlc-gate.sh lock {stage} {model} # 階段閘門通過時上鎖(覆寫既有鎖) -# sdlc-gate.sh unlock # 移除狀態檔 -# sdlc-gate.sh check # hook 模式(UserPromptSubmit):模型不符時注入繁中提醒 -# sdlc-gate.sh report # 印出 {sid} {stage} {model};無鎖不印 -# 缺資料時安靜降級,永遠 exit 0,不可中斷宿主 CLI。 +# sdlc-gate.sh — SDLC 階段模型閘門與 session 模型鎖(stage ∈ plan/analyze/implement/maintain)。 +# +# 判準是階段的「能力標籤」,不是模型名稱: +# - 標籤資料讀 $JSC_HOME/model-tags.tsv,由 jsc-cli/tools/model-tags.sh sync 產生。 +# - 目前模型 id 取自 transcript 記錄的實際值(lib.sh 的 transcript_model), +# 不採用模型自我回報——自我回報無法驗證,等同沒有閘門。 +# - 鎖存的是「階段的必要標籤」,不是「上鎖那一刻的模型」。鎖當時的模型只留作記錄, +# 否則用不合格的模型起跑就會把自己鎖成合格,閘門永遠通過。 +# +# 狀態檔:$JSC_HOME/sessions/{sid}.stage,單行「{stage}{必要標籤}{上鎖時的模型}」。 +# +# 用法: +# sdlc-gate.sh lock {stage} 階段閘門:比對實際模型與該階段必要標籤,通過才上鎖。 +# exit 0 = 通過並已上鎖;exit 1 = 未通過,呼叫端必須停止流程。 +# sdlc-gate.sh unlock 移除狀態檔(被擋住又確定要放行時的逃生門)。 +# sdlc-gate.sh check hook 模式(UserPromptSubmit):模型不符即擋下該輪提示。 +# sdlc-gate.sh report 印出 {sid} {stage} {必要標籤} {上鎖時的模型};無鎖不印。 +# +# exit code 例外:其他 jsc hook 一律 exit 0 不中斷宿主 CLI;本檔 check 是刻意的例外—— +# 鎖存在且模型不符時 exit 2 擋下該輪提示。只用提示注入的話模型可以無視,閘門形同虛設。 +# 無鎖、或資料不足無法判定時,仍照舊 exit 0 安靜降級。 HERE=$(dirname "$0"); . "$HERE/lib.sh" read_stdin sid=$(session_id) state="$JSC_HOME/sessions/$sid.stage" +TAGS_TSV="$JSC_HOME/model-tags.tsv" +STAGES="plan analyze implement maintain" + +# --- 標籤查詢(資料來源:model-tags.tsv) --- + +# 某階段的必要標籤;不限標籤的階段回傳 any。查不到不輸出。 +stage_tags() { # $1=階段 + [ -s "$TAGS_TSV" ] || return 0 + awk -F'\t' -v s="$1" '$1 == "stage" && $2 == s { print $3; exit }' "$TAGS_TSV" +} + +# 某模型的能力標籤。模型鍵與實際 id 雙向包含即視為同一家族 +#(例:表列 claude-haiku-4-5 對得上 claude-haiku-4-5-20251001);多筆命中取最長鍵。 +model_tags() { # $1=模型 id + [ -s "$TAGS_TSV" ] || return 0 + 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 } + ' "$TAGS_TSV" +} + +# 必要標籤中,該模型缺少的部分(逗號分隔);全部具備則不輸出。 +missing_tags() { # $1=必要標籤 $2=模型標籤 + awk -v req="$1" -v have="$2" ' + 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 + }' +} + +# 具備某組必要標籤的模型清單,用來告訴使用者可以切去哪個模型。 +eligible_models() { # $1=必要標籤 + [ -s "$TAGS_TSV" ] || return 0 + awk -F'\t' -v req="$1" ' + $1 == "model" { + split("", owned, ":") + n = split($3, h, ","); for (i = 1; i <= n; i++) owned[h[i]] = 1 + n = split(req, r, ","); ok = 1 + for (i = 1; i <= n; i++) if (!(r[i] in owned)) ok = 0 + if (ok) out = (out == "" ? $2 : out "、" $2) + } + END { print out } + ' "$TAGS_TSV" +} + +# 目前模型:transcript 實際值 > stdin JSON model > JSC_MODEL > ~/.claude/settings.json。 +# transcript 排最前面,因為那是唯一可驗證的實際值,其餘都只是宣告值。 +current_model() { + m=$(transcript_model) + [ -n "$m" ] || m=$(json_str model) + [ -n "$m" ] || m="${JSC_MODEL:-}" + if [ -z "$m" ] && { [ -z "${JSC_CLI:-}" ] || [ "${JSC_CLI:-}" = "claude" ]; }; then + m=$(tr -d '\n' < "$HOME/.claude/settings.json" 2>/dev/null \ + | sed -n 's/.*"model"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n1) + fi + printf '%s' "$m" +} case "${1:-check}" in lock) - # 需要 stage 與 model 兩個參數,缺一就安靜降級 - if [ -n "${2:-}" ] && [ -n "${3:-}" ]; then - printf '%s %s\n' "$2" "$3" > "$state" 2>/dev/null || true - fi ;; + stage="${2:-}" + case " $STAGES " in + *" $stage "*) ;; + *) echo "[jsc][SDLC 閘門][ERR]:階段須為 $STAGES 之一,收到「${stage:-空值}」。" >&2; exit 1 ;; + esac + + req=$(stage_tags "$stage") + if [ -z "$req" ]; then + echo "[jsc][SDLC 閘門][ERR]:讀不到階段「$stage」的必要標籤($TAGS_TSV 不存在或缺該階段)。請先執行 jsc-cli/tools/model-tags.sh sync 再重跑本階段,本次不進行任何工作。" >&2 + exit 1 + fi + + cur=$(current_model) + if [ -z "$cur" ]; then + echo "[jsc][SDLC 閘門][ERR]:判定不出目前實際使用的模型,無法驗證是否符合階段「$stage」。請確認 transcript 可讀,或設定 JSC_MODEL 後重跑,本次不進行任何工作。" >&2 + exit 1 + fi + + if [ "$req" != "any" ]; then + have=$(model_tags "$cur") + if [ -z "$have" ]; then + echo "[jsc][SDLC 閘門][ERR]:模型「$cur」不在能力標籤表上,無法判定是否夠格跑階段「$stage」(需要 $req)。請把該模型補進 jsc-cli/references/model-tags.md 後執行 model-tags.sh sync,本次不進行任何工作。" >&2 + exit 1 + fi + miss=$(missing_tags "$req" "$have") + if [ -n "$miss" ]; then + ok=$(eligible_models "$req") + echo "[jsc][SDLC 閘門][ERR]:階段「$stage」需要標籤「$req」,模型「$cur」缺少「$miss」。請切換到下列任一模型後重跑:${ok:-(表上無合格模型,請補表)}。本次不進行任何工作。" >&2 + exit 1 + fi + fi + + printf '%s\t%s\t%s\n' "$stage" "$req" "$cur" > "$state" 2>/dev/null || { + echo "[jsc][SDLC 閘門][ERR]:寫不進狀態檔 $state,階段鎖未生效。" >&2; exit 1; } + echo "[jsc][SDLC 閘門][OK]:階段「$stage」通過(需要 $req,目前模型 $cur),已上鎖。" + exit 0 ;; + unlock) - rm -f "$state" 2>/dev/null || true ;; + rm -f "$state" 2>/dev/null || true + exit 0 ;; + check) [ -f "$state" ] || exit 0 - stage=$(sed -n '1s/ .*//p' "$state" 2>/dev/null) - locked=$(sed -n '1s/^[^ ]* //p' "$state" 2>/dev/null) - [ -n "$stage" ] && [ -n "$locked" ] || exit 0 - # 目前模型判定順序:stdin JSON model > JSC_MODEL > ~/.claude/settings.json(僅 JSC_CLI 為 claude 或未設定時) - cur=$(json_str model) - [ -n "$cur" ] || cur="${JSC_MODEL:-}" - if [ -z "$cur" ] && { [ -z "${JSC_CLI:-}" ] || [ "${JSC_CLI:-}" = "claude" ]; }; then - cur=$(tr -d '\n' < "$HOME/.claude/settings.json" 2>/dev/null \ - | sed -n 's/.*"model"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n1) + stage=$(cut -f1 "$state" 2>/dev/null | head -n1) + req=$(cut -f2 "$state" 2>/dev/null | head -n1) + # 舊格式(空白分隔、無必要標籤欄)讀不出 req,安靜降級不擋。 + [ -n "$stage" ] && [ -n "$req" ] && [ "$stage" != "$req" ] || exit 0 + [ "$req" = "any" ] && exit 0 + + cur=$(current_model) + # 判定不出模型時只提醒,不擋——否則使用者會被鎖在無法送出提示的狀態。 + if [ -z "$cur" ]; then + echo "[jsc] SDLC 階段「${stage}」需要標籤「${req}」,但判定不出目前模型。請自行確認模型是否合格。" + exit 0 fi - [ -n "$cur" ] || exit 0 - # 別名相容:一方包含另一方就視為同一模型(例:opus 對 claude-opus-4-6) - case "$cur" in *"$locked"*) exit 0 ;; esac - case "$locked" in *"$cur"*) exit 0 ;; esac - echo "[jsc] SDLC 模型鎖:階段「${stage}」已鎖定模型「${locked}」,目前模型「${cur}」不符。請拒絕執行任何 SDLC 工作,並請使用者切回模型「${locked}」後再繼續。此鎖會在下一個 SDLC 階段閘門執行時解除。" ;; + + have=$(model_tags "$cur") + if [ -z "$have" ]; then + echo "[jsc][SDLC 閘門][ERR]:模型「${cur}」不在能力標籤表上,無法確認是否夠格跑階段「${stage}」(需要 ${req})。請補進 jsc-cli/references/model-tags.md 並執行 model-tags.sh sync;確定要放行請執行 jsc-hooks/hooks/sdlc-gate.sh unlock。本輪提示已擋下。" >&2 + exit 2 + fi + + miss=$(missing_tags "$req" "$have") + [ -z "$miss" ] && exit 0 + + ok=$(eligible_models "$req") + echo "[jsc][SDLC 閘門][ERR]:SDLC 階段「${stage}」需要標籤「${req}」,目前模型「${cur}」缺少「${miss}」。請切換到下列任一模型後重送:${ok:-(表上無合格模型,請補表)}。要結束本階段的鎖請執行 jsc-hooks/hooks/sdlc-gate.sh unlock。本輪提示已擋下。" >&2 + exit 2 ;; + report) if [ -f "$state" ]; then - line=$(sed -n '1p' "$state" 2>/dev/null) + line=$(sed -n '1p' "$state" 2>/dev/null | tr '\t' ' ') [ -n "$line" ] && echo "$sid $line" - fi ;; + fi + exit 0 ;; esac exit 0 diff --git a/skills/hooks-install/SKILL.md b/skills/hooks-install/SKILL.md index 0de658f..e59f4a7 100644 --- a/skills/hooks-install/SKILL.md +++ b/skills/hooks-install/SKILL.md @@ -6,7 +6,8 @@ description: Wire jsc hooks (STE100 guard, session timer, skill usage logger, SD # hooks-install — wire jsc hooks into every installed CLI Goal: make the four hooks (`ste100-guard.sh`, `session-timer.sh`, `skill-usage.sh`, `sdlc-gate.sh`) effective in every CLI. -Claude wiring is automatic via `hooks.json`. On codex and kiro the SDLC gate degrades to the skill-step check only; the lock file still works because the SDLC skills call `sdlc-gate.sh lock` directly. +Claude wiring is automatic via `hooks.json`. On codex and kiro the SDLC gate degrades to the skill-step check only; the lock file still works because the SDLC skills call `sdlc-gate.sh lock {stage}` directly — that call is where the capability-tag comparison happens, so the gate keeps its force even where the prompt hook cannot be wired. +The gate needs `$JSC_HOME/model-tags.tsv`; when it is missing, report that `jsc-cli:models` (or `jsc-cli/tools/model-tags.sh sync`) must run once, because `sdlc-gate.sh lock` refuses to lock without it. The detailed flow **MUST run as a sub agent**; the main agent only reports the summary. ## Steps From 0d1671cae19537d8ac7fbc78771897100b6f36ae Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 24 Aug 2026 16:05:18 +0800 Subject: [PATCH 09/10] =?UTF-8?q?docs(README):=20=E6=9B=B4=E6=96=B0=20sdlc?= =?UTF-8?q?-gate=20=E9=96=98=E9=96=80=E8=A1=8C=E7=82=BA=E8=88=87=20JSC=5FM?= =?UTF-8?q?ODEL=20=E5=84=AA=E5=85=88=E5=BA=8F=E8=AA=AA=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index efa1b56..624c5c2 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/jsc.git),安 | `hooks/ste100-guard.sh` | UserPromptSubmit | 注入 STE100 繁體中文輸出規則(hook > prompt 強制層) | | `hooks/session-timer.sh` | SessionStart / Stop / SessionEnd | 記錄工作階段起訖;`report` 子指令供 `jsc-log:worklog` 取花費時間 | | `hooks/skill-usage.sh` | PostToolUse(Skill) | 記錄技能使用與呼叫鏈到 `$JSC_HOME/usage/*.jsonl`,供 `jsc-log:stats` 統計 | -| `hooks/sdlc-gate.sh` | UserPromptSubmit | SDLC 每 session 模型鎖:`lock`/`unlock` 由 jsc-sdlc 階段技能呼叫,`check` 在模型與鎖不符時注入拒絕提醒 | +| `hooks/sdlc-gate.sh` | UserPromptSubmit | SDLC 階段能力標籤閘門與模型鎖:`lock {stage}` 由 jsc-sdlc 階段技能呼叫,從 transcript 讀出實際模型 id 比對該階段必要標籤(`$JSC_HOME/model-tags.tsv`),不符就拒絕上鎖;`check` 在模型不符時以 exit 2 擋下該輪提示(其他 hook 一律 exit 0,此處是刻意例外);`unlock` 為逃生門 | Claude 由 `hooks/hooks.json` 自動接線;其他 CLI 用 `hooks-install` 技能接線、改裝包裝啟動器,或降級為規則檔。 @@ -63,7 +63,7 @@ Claude 由 `hooks/hooks.json` 自動接線;其他 CLI 用 `hooks-install` 技 | --- | --- | --- | | `JSC_HOME` | Hook 資料目錄 | 預設 `~/.jsc` | | `JSC_CLI` / `JSC_SESSION_ID` / `JSC_SKILL` | 非 Claude CLI 接線時由 `tools/jsc-wrap.sh` 或接線設定提供 | 安靜降級 | -| `JSC_MODEL` | 非 Claude CLI 的目前模型,供 `sdlc-gate.sh check` 比對 | 改讀 `~/.claude/settings.json`,再不行就安靜降級 | +| `JSC_MODEL` | 非 Claude CLI 的目前模型,供 `sdlc-gate.sh` 比對;優先序在 transcript 實際值與 stdin `model` 之後 | 改讀 `~/.claude/settings.json`,再不行就安靜降級 | ## 相關 domain From cacd23486971ce779677db2daae6e6a10963262c Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 24 Aug 2026 16:05:18 +0800 Subject: [PATCH 10/10] =?UTF-8?q?chore(plugin=20=E7=89=88=E6=9C=AC):=20?= =?UTF-8?q?=E4=B8=89=E4=BB=BD=20manifest=20=E5=8D=87=E7=89=88=E8=87=B3=200?= =?UTF-8?q?.0.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index dcbb8d3..b6257ed 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.0.4", + "version": "0.0.5", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 8d576b3..f2b485d 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.0.4", + "version": "0.0.5", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖", "skills": "./skills" } diff --git a/plugin.json b/plugin.json index 9179cb9..04c9168 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.0.4", + "version": "0.0.5", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖", "skills": "./skills/" }