What:新增第八支 hook `hooks/restart-gate.sh`。hook 模式在 `$JSC_HOME/restart-required` 存在時以 exit 2 擋下 jsc 技能呼叫,另有三個子命令:`require {模式} [{domain}...]` 寫入狀態檔掛上閘門、`clear` 清除狀態檔、`report` 印出狀態檔內容。`hooks/hooks.json` 把它接到 PreToolUse(Skill),排在 `version-guard.sh` 前面。`hooks/session-timer.sh` 的 `start` 與 `restart` 在判定為新工作階段時轉呼叫 `clear`。
Why:部署換掉的是磁碟上的技能檔,正在跑的 CLI 行程載入的還是舊版——SKILL.md、hook 腳本與 tools 都在啟動當下讀進記憶體。這段落差期間跑技能,改動看起來沒生效,人會以為部署失敗又重跑一次。所以部署收尾要求重新啟動,這道閘門負責讓「還沒重啟就繼續用技能」擋在門外。
How:判定看的是「檔案在不在」,欄位只用在擋人訊息上——欄位缺了只讓訊息少幾個字,不影響判定。狀態檔用純文字 `key=value`(`at`、`mode`、`domains`、`cli`),格式與工作包狀態檔同一套,`jsc-hooks` 與 `jsc-cli` 兩邊各自實作也對得上。放行原則比照 `version-guard.sh`:只擋確定違規,狀態檔讀不到、技能名取不到、工具名不是 `Skill`、技能不是 `jsc-*:*` 一律 exit 0,沒有證據時擋下等於停掉每一次技能呼叫。九支豁免(`jsc-cli:deploy`、`jsc-hooks:hooks-install`、`jsc-gitea:wiki`、`jsc-log:worklog`、`jsc-log:learn`、`jsc-meta:*`、`jsc-ask:ask`、`jsc-git:pr`、`jsc-git:commit`)的理由是同一件事:部署後還要寫得完技能組異動報告與工作日誌,整批擋下去「先重啟」與「先寫完報告」會互相打死。後三支自己不是收尾規則的主體,是為了讓前六支走得完才補進來的——清單認的是技能名,不是呼叫鏈。清除交給 `session-timer.sh`,不由本檔自己判:新舊工作階段的判準(`sessions/{sid}.start` 在不在)只有那支腳本知道,兩邊各寫一份就會漂移;續接同一階段走不到那一段,閘門就一路留到真的重新啟動。`require`、`clear`、`report` 都不讀標準輸入,只有 hook 模式讀,工具端呼叫一律再補 `</dev/null`,理由與 `sdlc-gate.sh` 相同。接線排在 `version-guard.sh` 前面:還沒重啟的舊版比落後一個版號更該先攔。不認得的子命令一律安靜 exit 0,不中斷宿主 CLI。擋人訊息依實際 CLI 給重啟方式,並附上豁免清單與逃生門 `JSC_RESTART_GATE=off`。
Who:部署後的第一次技能呼叫,以及 `jsc-cli:deploy` 收尾寫入狀態檔的那一端。
49 lines
2.5 KiB
Bash
Executable File
49 lines
2.5 KiB
Bash
Executable File
#!/usr/bin/env sh
|
||
# session-timer.sh — 記錄工作階段花費時間(供 jsc-log:worklog 取用)。
|
||
# 用法:
|
||
# session-timer.sh start # SessionStart:記錄起始時間(已有紀錄就不動)
|
||
# session-timer.sh restart # SessionStart:一律覆寫起始時間
|
||
# session-timer.sh mark # Stop/SessionEnd:更新最後活動時間
|
||
# session-timer.sh report [sid] # 印出 {sid} {seconds};無紀錄印 0
|
||
#
|
||
# start 與 restart 的差別在「同一個 session id 會不會重複開始」:
|
||
# start 給 Claude 這種每個工作階段都有自己 session id 的 CLI。續接同一階段時
|
||
# SessionStart 會再觸發一次,覆寫起始時間會讓花費時間歸零。
|
||
# restart 給接不到 session id 的 CLI(kiro)。那些 CLI 的紀錄共用 default,
|
||
# 不覆寫就會把上一個工作階段的起始時間算進來,花費時間虛胖。
|
||
#
|
||
# 這兩個子命令另外兼一件事:判定為「新的工作階段」時清除部署後的重啟閘門
|
||
# (restart-gate.sh clear)。新工作階段代表 CLI 行程是新起的,新版技能組一定已經載入。
|
||
# 判準只有這裡知道——start 分支的「起始檔不存在」就是這個 session id 第一次開始,
|
||
# 所以清除掛在這裡,不在 restart-gate.sh 裡自己再判一次。
|
||
HERE=$(dirname "$0"); . "$HERE/lib.sh"
|
||
read_stdin
|
||
sid=$(session_id)
|
||
|
||
# 放下部署後的重啟閘門。狀態檔的路徑與格式只留在 restart-gate.sh,這裡不碰檔案。
|
||
# 一律 </dev/null:clear 不讀標準輸入,但這裡的標準輸入是宿主餵進來的管線,不關掉會卡住。
|
||
clear_restart_gate() {
|
||
sh "$HERE/restart-gate.sh" clear </dev/null 2>/dev/null || true
|
||
}
|
||
|
||
case "${1:-mark}" in
|
||
start)
|
||
f="$JSC_HOME/sessions/$sid.start"
|
||
if [ ! -f "$f" ]; then
|
||
now_epoch > "$f"
|
||
clear_restart_gate # 起始檔不存在=這個工作階段第一次開始,也就是行程新起的那一次
|
||
fi ;;
|
||
restart)
|
||
now_epoch > "$JSC_HOME/sessions/$sid.start"
|
||
rm -f "$JSC_HOME/sessions/$sid.end"
|
||
clear_restart_gate ;; # 接不到 session id 的 CLI 每次工作階段開始都算新的,一律清
|
||
mark)
|
||
now_epoch > "$JSC_HOME/sessions/$sid.end" ;;
|
||
report)
|
||
rsid="${2:-$sid}"
|
||
s=$(cat "$JSC_HOME/sessions/$rsid.start" 2>/dev/null || echo "")
|
||
e=$(cat "$JSC_HOME/sessions/$rsid.end" 2>/dev/null || now_epoch)
|
||
if [ -n "$s" ]; then echo "$rsid $((e - s))"; else echo "$rsid 0"; fi ;;
|
||
esac
|
||
exit 0
|