From f83e1c542ff94ca73a5f0112931d6e61753be7ef Mon Sep 17 00:00:00 2001 From: Jeffery Date: Mon, 7 Sep 2026 12:39:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(wire-cli):=20=E9=80=90=E6=94=AF=E9=BB=9E?= =?UTF-8?q?=E5=90=8D=E6=94=B9=E6=88=90=E5=BE=9E=E6=8E=A5=E7=B7=9A=E9=82=A3?= =?UTF-8?q?=E4=B8=80=E9=82=8A=E6=8A=BD=E5=90=8D=E5=96=AE=EF=BC=8C=E4=BA=94?= =?UTF-8?q?=E6=94=AF=20CLI=20=E9=83=BD=E9=A9=97=E5=BE=97=E5=88=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 第十支 hook 上線之後實測發現:claude 與 kiro 的盤點點名得出它,codex、 copilot、antigravity 三段的盤點卻各自只驗自己寫死的那兩支。那三支的接線 盤點看不出第十支在不在,而 smoke 那一行還自稱「十支 hook」——兩句話都是 真的,範圍不同,讀的人分不出來。 根因是「接線寫了哪幾支」與「盤點驗了哪幾支」本來是兩份手寫清單。加一支 hook 要記得回來改第二個地方,而漏改的那一天不會有任何東西叫。 改成從接線那一邊實際會寫出去的內容抽名字,兩邊只留一份清單: - codex 讀的是從 hooks/hooks.json 推導出來的複本,所以名單取自來源那一份, 十支全驗;推導漏掉一支時,盤點說得出漏了哪一支。 - copilot 與 antigravity 只接得上兩道閘門,名單取自各自的條目產生函式。 - copilot 原本一支 hook 都沒點名,只驗「hooks 鍵在」——鍵在而某一支條目掉了 照樣回 present,那正是這一整支腳本一直在防的形態。 codex 接線後的驗證也一起改:原本只確認那兩道閘門有接上,現在逐支對照來源。 推導是整份複製再改 matcher,漏掉任何一支都算推導壞了。 Co-Authored-By: Claude Opus 5 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- tools/wire-cli.sh | 36 ++++++++++++++++++++++++++++++++---- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 6c69d1a..861376a 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.4.8", + "version": "0.4.9", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍守門、繁中編碼守門、部署後強制重啟、寫入與提交閘門", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index f21c608..014b606 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "hooks": "./hooks/codex-hooks.json", "name": "jsc-hooks", - "version": "0.4.8", + "version": "0.4.9", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍守門、繁中編碼守門、部署後強制重啟、寫入與提交閘門", "skills": "./skills", "jsc": { diff --git a/plugin.json b/plugin.json index 7729ec4..25b6e70 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.4.8", + "version": "0.4.9", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍守門、繁中編碼守門、部署後強制重啟、寫入與提交閘門", "skills": "./skills/", "jsc": { diff --git a/tools/wire-cli.sh b/tools/wire-cli.sh index 0ac8a7b..adb0305 100755 --- a/tools/wire-cli.sh +++ b/tools/wire-cli.sh @@ -282,6 +282,19 @@ codex_derive_hooks() { # $1=來源 hooks.json # 條目形態照這台機器上既有的那一筆第三方設定(type、bash、timeoutSec),不套 Claude 的形狀。 # 指令前綴帶 JSC_CLI=copilot:閘門靠 cli_name() 認代號才取得到技能名與阻擋形態,沒設就一律 # 安靜放行,matcher 對、位置對、卻一次都擋不下來。別名那條路只在走別名啟動時才成立,不能靠它。 +# 列出一份檔案或一段設定裡引用到的 hook 腳本名,一行一個、去重。 +# +# 這一支存在的理由是「接線寫了哪幾支」與「盤點驗了哪幾支」本來是兩份手寫清單。 +# 2026-09-07 實測踩到:新加第十支 hook 之後,claude 與 kiro 的盤點點名得出它, +# codex、copilot、antigravity 三段的盤點卻各自只驗自己寫死的那兩支——那三支的接線 +# 盤點看不出第十支在不在,而 smoke 那一行還自稱「十支 hook」。兩句話都是真的, +# 範圍不同,讀的人分不出來。 +# 改成從接線那一邊實際會寫出去的內容抽名字,兩邊就只有一份清單:往後加一支 hook, +# 盤點自動跟著驗,不必記得回來改第二個地方。 +hook_scripts_of() { # 讀標準輸入,印出 {名稱}.sh + grep -oE '[a-z][a-z0-9-]*\.sh' 2>/dev/null | sort -u +} + copilot_hook_entries() { cat </dev/null then st_item "$_h" "$claude_hooks" present @@ -2418,7 +2434,9 @@ if [ "$action" = status ]; then if [ -f "$cx_hooks" ] && grep -qF '"matcher": "Skill"' "$cx_hooks" 2>/dev/null then st_item codex-no-skill-matcher "$cx_hooks" missing else st_item codex-no-skill-matcher "$cx_hooks" present; fi - for _g in restart-gate.sh version-guard.sh; do + # 逐支對照來源那一份。codex 讀的是從 hooks/hooks.json 推導出來的複本,所以「該有哪幾支」 + # 的答案在來源檔裡,不在這裡寫死——推導漏掉一支的時候,這一項才說得出是漏了哪一支。 + for _g in $(hook_scripts_of <"$HOOKS/hooks.json"); do if [ -f "$cx_hooks" ] && grep -qF "$_g" "$cx_hooks" 2>/dev/null then st_item "codex-${_g%.sh}" "$cx_hooks" present else st_item "codex-${_g%.sh}" "$cx_hooks" missing; fi @@ -2446,6 +2464,13 @@ if [ "$action" = status ]; then if copilot_single_case_ok "$cp_settings" then st_item single-event-case "$cp_settings" present else st_item single-event-case "$cp_settings" missing; fi + # 逐支對照接線那一段自己會寫出去的條目。原本這一段一支 hook 都沒點名,只驗「hooks 鍵在」 + # ——鍵在而某一支條目掉了,照樣回 present,那正是這一整支腳本一直在防的形態。 + for _g in $(copilot_hook_entries | hook_scripts_of); do + if [ -f "$cp_settings" ] && grep -qF "$_g" "$cp_settings" 2>/dev/null + then st_item "${_g%.sh}" "$cp_settings" present + else st_item "${_g%.sh}" "$cp_settings" missing; fi + done # CLI 代號也是接線的一部分,理由同 codex 那一項:取不到代號的閘門一律安靜放行。 if [ -f "$cp_settings" ] && grep -qF 'JSC_CLI=copilot' "$cp_settings" 2>/dev/null then st_item cli-code "$cp_settings" present @@ -2483,7 +2508,8 @@ if [ "$action" = status ]; then if antigravity_flat_ok "$ag_hooks" then st_item preinvocation-flat "$ag_hooks" present else st_item preinvocation-flat "$ag_hooks" missing; fi - for _g in restart-gate.sh version-guard.sh; do + # 逐支對照接線那一段自己會寫出去的內容,不在這裡另寫一份清單。 + for _g in $(antigravity_hooks_block | hook_scripts_of); do if [ -f "$ag_hooks" ] && grep -qF "$_g" "$ag_hooks" 2>/dev/null then st_item "${_g%.sh}" "$ag_hooks" present else st_item "${_g%.sh}" "$ag_hooks" missing; fi @@ -2634,9 +2660,11 @@ case "$cli" in || fail "$cx_hooks 沒有 Bash matcher,codex 沒有 Skill 工具,擋不到技能載入那一次" grep -qF '"matcher": "Skill"' "$cx_hooks" 2>/dev/null \ && fail "$cx_hooks 還留著 Skill matcher,codex 沒有那個工具,那一組永遠不會被叫用" - for _g in restart-gate.sh version-guard.sh; do + # 逐支對照來源那一份,不只驗那兩道閘門:推導是整份複製再改 matcher,漏掉任何一支都算 + # 推導壞了,而漏掉的那一支在 codex 上就是永遠不會被叫用。 + for _g in $(hook_scripts_of <"$HOOKS/hooks.json"); do grep -qF "$_g" "$cx_hooks" 2>/dev/null \ - || fail "$cx_hooks 沒有接上 $_g,那道閘門在 codex 上不會生效" + || fail "$cx_hooks 沒有接上 $_g,那一支在 codex 上不會生效(來源是 $HOOKS/hooks.json,推導漏了它)" done # 代號單獨驗一項:少了它,兩道閘門認不出現在跑的是哪一支 CLI,技能名解不出來就整批安靜放行。 grep -qF 'JSC_CLI=codex' "$cx_hooks" 2>/dev/null \ -- 2.53.0