feat(sdlc-gate): 新增 SDLC 每 session 模型鎖 hook
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 <noreply@anthropic.com>
This commit is contained in:
+4
-1
@@ -4,7 +4,10 @@
|
|||||||
{ "hooks": [ { "type": "command", "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/session-timer.sh\" start" } ] }
|
{ "hooks": [ { "type": "command", "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/session-timer.sh\" start" } ] }
|
||||||
],
|
],
|
||||||
"UserPromptSubmit": [
|
"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": [
|
"Stop": [
|
||||||
{ "hooks": [ { "type": "command", "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/session-timer.sh\" mark" } ] }
|
{ "hooks": [ { "type": "command", "command": "sh \"${CLAUDE_PLUGIN_ROOT}/hooks/session-timer.sh\" mark" } ] }
|
||||||
|
|||||||
Executable
+46
@@ -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
|
||||||
Reference in New Issue
Block a user