tmuxclihook 0.5.14 → 0.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scripts/adapters/codex.sh +56 -4
- package/scripts/install-claude-hooks.sh +6 -0
- package/scripts/install-codex-hooks.sh +3 -0
- package/scripts/install-opencode-hooks.sh +3 -0
- package/scripts/lib-install-hooks.sh +26 -0
- package/scripts/lib-tmux-ai-status.sh +76 -6
- package/scripts/tmux-ai-status +3 -0
- package/src/check-detail.js +31 -0
- package/tmux-ai-hooks-status.tmux +2 -1
package/package.json
CHANGED
|
@@ -74,13 +74,65 @@ adapter_check_integrity() {
|
|
|
74
74
|
_CODEX_EVENTS="SessionStart UserPromptSubmit PreToolUse PermissionRequest PostToolUse Stop"
|
|
75
75
|
_CODEX_LEGACY_TOML="${CODEX_HOME:-$HOME/.codex}/hooks.toml"
|
|
76
76
|
|
|
77
|
+
# Hook 授信记录(trust 维度,check --json 用):codex 把 user 来源 hook 的授信写在
|
|
78
|
+
# 每个实例 config.toml 的 [hooks.state."<hooks.json 路径>:<下划线事件>:<组>:<hook>"]。
|
|
79
|
+
# 只判键是否存在——bash 无法复现 trusted_hash;因此:
|
|
80
|
+
# - 键缺失 = 该 hook 一定不会执行(强信号)
|
|
81
|
+
# - 键存在 ≠ 一定执行(hooks 被改写后旧键仍在、hash 失效,codex 会重新弹 Trust)
|
|
82
|
+
# config.toml 不存在 → trusted:null(无法判定)。
|
|
83
|
+
_CODEX_TRUST_EVENTS="session_start user_prompt_submit pre_tool_use permission_request post_tool_use stop"
|
|
84
|
+
|
|
85
|
+
_codex_trust_scan() { # $1=config.toml $2=hooks.json → 每行 "event<TAB>0|1"
|
|
86
|
+
local cfg="$1" hooks="$2" keys e
|
|
87
|
+
[ -f "$cfg" ] || return 1
|
|
88
|
+
# 只提取键名,绝不读取 trusted_hash 值
|
|
89
|
+
keys=$(sed -n 's/^\[hooks\.state\."\([^"]*\)"\].*/\1/p' "$cfg" 2>/dev/null)
|
|
90
|
+
for e in $_CODEX_TRUST_EVENTS; do
|
|
91
|
+
case "$keys" in
|
|
92
|
+
*"$hooks:$e:"*) printf '%s\t1\n' "$e" ;;
|
|
93
|
+
*) printf '%s\t0\n' "$e" ;;
|
|
94
|
+
esac
|
|
95
|
+
done
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# 汇总 $HOME/.codex 与所有 cc-switch 实例的授信状态(去重;config.toml 不存在则不收录,
|
|
99
|
+
# 当前实例不存在时 trusted:null)。只读。
|
|
100
|
+
_codex_trust_json() {
|
|
101
|
+
local cfg hooks rows e mark missing trusted seen=""
|
|
102
|
+
for cfg in "${HOME}/.codex/config.toml" "${HOME}"/.cc-switch/instances/codex/*/config.toml; do
|
|
103
|
+
[ -f "$cfg" ] || continue
|
|
104
|
+
[ "$cfg" = "$seen" ] && continue
|
|
105
|
+
seen="$cfg"
|
|
106
|
+
hooks="$(dirname "$cfg")/hooks.json"
|
|
107
|
+
rows=$(_codex_trust_scan "$cfg" "$hooks" 2>/dev/null) || continue
|
|
108
|
+
missing=""
|
|
109
|
+
while IFS=$'\t' read -r e mark; do
|
|
110
|
+
[ "$mark" = "0" ] && missing="${missing}${missing:+,}$e"
|
|
111
|
+
done <<EOF
|
|
112
|
+
$rows
|
|
113
|
+
EOF
|
|
114
|
+
if [ -n "$missing" ]; then trusted=0; else trusted=1; fi
|
|
115
|
+
printf '%s\t%s\t%s\t%s\n' "$cfg" "$hooks" "$trusted" "$missing"
|
|
116
|
+
done | jq -R -s --arg cur "${CODEX_HOME:-$HOME/.codex}/config.toml" '
|
|
117
|
+
split("\n") | map(select(length > 0) | split("\t")) as $rows
|
|
118
|
+
| [$rows[] | {configFile: .[0], hooksFile: .[1],
|
|
119
|
+
trusted: (.[2] == "1"),
|
|
120
|
+
missingEvents: (if .[3] == "" then [] else (.[3] | split(",")) end)}] as $inst
|
|
121
|
+
| ($inst | map(select(.configFile == $cur)) | first) as $cur_obj
|
|
122
|
+
| {configFile: $cur,
|
|
123
|
+
trusted: (if $cur_obj then $cur_obj.trusted else null end),
|
|
124
|
+
missingEvents: (if $cur_obj then $cur_obj.missingEvents else [] end),
|
|
125
|
+
instances: $inst}'
|
|
126
|
+
}
|
|
127
|
+
|
|
77
128
|
# 明细检查(仅 install wrapper 的 check --json 调用;与 claude adapter 同构)。
|
|
78
129
|
# 差异:无 cc-switch 分支(hooks.json 即文件真源,mode 恒 file)、6 事件、
|
|
79
130
|
# 匹配串为 "tmux-ai-status codex"。失败输出空 events 降级片段,不 exit 非 0。
|
|
80
131
|
adapter_check_detail() {
|
|
81
|
-
local src="$ADAPTER_HOOKS_FILE" pairs rows line ev p a
|
|
132
|
+
local src="$ADAPTER_HOOKS_FILE" pairs rows line ev p a trust_json
|
|
133
|
+
trust_json=$(_codex_trust_json)
|
|
82
134
|
if [ ! -f "$src" ]; then
|
|
83
|
-
printf '{"mode":"file","events":{},"plugin":null,"registeredPaths":[]}\n'
|
|
135
|
+
printf '{"mode":"file","events":{},"plugin":null,"registeredPaths":[],"trust":%s}\n' "$trust_json"
|
|
84
136
|
return 0
|
|
85
137
|
fi
|
|
86
138
|
pairs=$(jq -r '.hooks as $h |
|
|
@@ -98,7 +150,7 @@ adapter_check_detail() {
|
|
|
98
150
|
done <<EOF
|
|
99
151
|
$pairs
|
|
100
152
|
EOF
|
|
101
|
-
jq -R -s --arg mode "file" --arg events "$_CODEX_EVENTS" '
|
|
153
|
+
jq -R -s --arg mode "file" --arg events "$_CODEX_EVENTS" --argjson trust "$trust_json" '
|
|
102
154
|
(reduce ($events | split(" ")[]) as $e
|
|
103
155
|
({}; .[$e] = {registered: 0, alive: 0, scriptPaths: []})) as $base
|
|
104
156
|
| reduce (split("\n") | .[] | select(length > 0) | split("\t")) as $r (
|
|
@@ -109,7 +161,7 @@ EOF
|
|
|
109
161
|
scriptPaths: ((.[$r[0]].scriptPaths + [$r[1]]) | unique)
|
|
110
162
|
})
|
|
111
163
|
| {mode: $mode, events: ., plugin: null,
|
|
112
|
-
registeredPaths: ([.[] | .scriptPaths[]] | unique)}
|
|
164
|
+
registeredPaths: ([.[] | .scriptPaths[]] | unique), trust: $trust}
|
|
113
165
|
' <<<"$rows"
|
|
114
166
|
}
|
|
115
167
|
|
|
@@ -45,6 +45,9 @@ esac
|
|
|
45
45
|
case "${1:-install}" in
|
|
46
46
|
uninstall)
|
|
47
47
|
adapter_uninstall_hooks
|
|
48
|
+
# 先清安装意图再写卸载意图:suppress 写失败时 errexit 会退出,此顺序
|
|
49
|
+
# 保证 installed 不残留(残留会让 _refresh 把刚卸载的 hooks 装回来)。
|
|
50
|
+
_hooks_installed_clear claude || true
|
|
48
51
|
_hooks_suppress_write claude
|
|
49
52
|
;;
|
|
50
53
|
check)
|
|
@@ -66,15 +69,18 @@ case "${1:-install}" in
|
|
|
66
69
|
# install 收敛。结构漂移(async/matcher 被外部改)不在判定内,auto 跳过
|
|
67
70
|
# 不修复,显式 install 仍强收敛。
|
|
68
71
|
if adapter_check_converged; then
|
|
72
|
+
_hooks_installed_ensure claude || true
|
|
69
73
|
echo "claude hooks already converged (skip install)"
|
|
70
74
|
exit 0
|
|
71
75
|
fi
|
|
72
76
|
adapter_install_hooks
|
|
73
77
|
_hooks_suppress_clear claude || true
|
|
78
|
+
_hooks_installed_ensure claude || true
|
|
74
79
|
;;
|
|
75
80
|
install)
|
|
76
81
|
adapter_install_hooks
|
|
77
82
|
_hooks_suppress_clear claude || true
|
|
83
|
+
_hooks_installed_ensure claude || true
|
|
78
84
|
;;
|
|
79
85
|
*)
|
|
80
86
|
echo "Usage: $0 [install|uninstall|check [--json]|auto]" >&2
|
|
@@ -40,6 +40,8 @@ esac
|
|
|
40
40
|
case "${1:-install}" in
|
|
41
41
|
uninstall)
|
|
42
42
|
adapter_uninstall_hooks
|
|
43
|
+
# 先清安装意图再写卸载意图(理由同 claude wrapper)
|
|
44
|
+
_hooks_installed_clear codex || true
|
|
43
45
|
_hooks_suppress_write codex
|
|
44
46
|
;;
|
|
45
47
|
check)
|
|
@@ -55,6 +57,7 @@ case "${1:-install}" in
|
|
|
55
57
|
install)
|
|
56
58
|
adapter_install_hooks
|
|
57
59
|
_hooks_suppress_clear codex || true
|
|
60
|
+
_hooks_installed_ensure codex || true
|
|
58
61
|
;;
|
|
59
62
|
*)
|
|
60
63
|
echo "Usage: $0 [install|uninstall|check [--json]]" >&2
|
|
@@ -40,6 +40,8 @@ esac
|
|
|
40
40
|
case "${1:-install}" in
|
|
41
41
|
uninstall)
|
|
42
42
|
adapter_uninstall_hooks
|
|
43
|
+
# 先清安装意图再写卸载意图(理由同 claude wrapper)
|
|
44
|
+
_hooks_installed_clear opencode || true
|
|
43
45
|
_hooks_suppress_write opencode
|
|
44
46
|
;;
|
|
45
47
|
check)
|
|
@@ -55,6 +57,7 @@ case "${1:-install}" in
|
|
|
55
57
|
install)
|
|
56
58
|
adapter_install_hooks
|
|
57
59
|
_hooks_suppress_clear opencode || true
|
|
60
|
+
_hooks_installed_ensure opencode || true
|
|
58
61
|
;;
|
|
59
62
|
*)
|
|
60
63
|
echo "Usage: $0 [install|uninstall|check [--json]]" >&2
|
|
@@ -86,6 +86,32 @@ _hooks_suppress_clear() {
|
|
|
86
86
|
fi
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
# --- 安装意图标记(installed marker)---
|
|
90
|
+
# 显式 install / claude auto(含已收敛跳过)→ 写;显式 uninstall → 删。
|
|
91
|
+
# 语义 = 用户装过该工具;跨工具自修复(_maybe_repair_other_tools)据此决定
|
|
92
|
+
# hooks 丢失时是否允许自动重装。无标记 + check 失败时绝不安装——尊重
|
|
93
|
+
# 「codex/opencode 非人人安装」的现状。
|
|
94
|
+
# ⚠️ 与 suppressed 同属 _SUPPRESS_DIR 两处同步约束(另一处见 lib-tmux-ai-status.sh)。
|
|
95
|
+
_hooks_installed_file() { printf '%s/installed-%s' "$_SUPPRESS_DIR" "$1"; }
|
|
96
|
+
_hooks_installed_write() {
|
|
97
|
+
if ! mkdir -p "$_SUPPRESS_DIR" 2>/dev/null \
|
|
98
|
+
|| ! date +%s > "$(_hooks_installed_file "$1")" 2>/dev/null; then
|
|
99
|
+
echo "WARN: 安装意图标记写入失败——该工具 hooks 丢失后不会自动修复;" \
|
|
100
|
+
"请检查 $_SUPPRESS_DIR 可写性" >&2
|
|
101
|
+
return 1
|
|
102
|
+
fi
|
|
103
|
+
}
|
|
104
|
+
_hooks_installed_clear() {
|
|
105
|
+
if [ -e "$(_hooks_installed_file "$1")" ] \
|
|
106
|
+
&& ! rm -f "$(_hooks_installed_file "$1")" 2>/dev/null; then
|
|
107
|
+
echo "WARN: 安装意图标记删除失败(卸载仍由 suppressed 标记兜底)" >&2
|
|
108
|
+
return 1
|
|
109
|
+
fi
|
|
110
|
+
}
|
|
111
|
+
_hooks_installed_ensure() { # 已登记则不动,避免 mtime 抖动
|
|
112
|
+
[ -e "$(_hooks_installed_file "$1")" ] || _hooks_installed_write "$1"
|
|
113
|
+
}
|
|
114
|
+
|
|
89
115
|
# --- 安装/卸载读-改-写互斥锁 ---
|
|
90
116
|
# 写路径的并发方:TUI 显式装 / tmux 入口 auto / SessionStart 自修复 / _maybe_repair_hooks /
|
|
91
117
|
# drift 收敛——五方都经 wrapper dispatch,锁在此层一处覆盖三 adapter。
|
|
@@ -10,6 +10,17 @@ _STATUS_DIR="${TMUX_AI_STATUS_DIR:-/tmp/ai-status}"
|
|
|
10
10
|
_SUPPRESS_DIR="${TMUX_AI_SUPPRESS_DIR:-${XDG_CONFIG_HOME:-${HOME}/.config}/tmuxclihook}"
|
|
11
11
|
STATUS_COLOR="#F1FA8C"
|
|
12
12
|
|
|
13
|
+
# 安装意图标记路径(与 lib-install-hooks.sh 的 _hooks_installed_file 同构)。
|
|
14
|
+
# 事件路径不 source 安装库,故此处独立实现;目录定义同 suppressed 两处同步。
|
|
15
|
+
# 语义:用户显式装过该工具 → 跨工具自修复允许在其 hooks 丢失时自动重装。
|
|
16
|
+
_installed_marker_path() { printf '%s/installed-%s' "$_SUPPRESS_DIR" "$1"; }
|
|
17
|
+
_installed_marker_write() {
|
|
18
|
+
local f
|
|
19
|
+
f=$(_installed_marker_path "$1") || return 1
|
|
20
|
+
mkdir -p "$_SUPPRESS_DIR" 2>/dev/null || return 1
|
|
21
|
+
date +%s > "$f" 2>/dev/null
|
|
22
|
+
}
|
|
23
|
+
|
|
13
24
|
# --- 日志模块 ---
|
|
14
25
|
source "${_LIB_DIR}/lib-tmux-ai-log.sh"
|
|
15
26
|
|
|
@@ -907,7 +918,15 @@ _cleanup_stale_panes() {
|
|
|
907
918
|
local ps_snap
|
|
908
919
|
ps_snap=$(_ps_process_snapshot)
|
|
909
920
|
|
|
910
|
-
|
|
921
|
+
# 动态追加 monitor seen 字段(与 build_all_status 同一组名字,复用其收集器)。
|
|
922
|
+
# launcher/daemon 场景工具进程可能脱离 pane 进程树,进程树 BFS 看不到;
|
|
923
|
+
# seen=1 是 monitor 用 launcher 进程树探测得出的「工具确实存活」信号。
|
|
924
|
+
local seen_fmt="" _seen_name
|
|
925
|
+
for _seen_name in $(_collect_ai_process_names); do
|
|
926
|
+
seen_fmt="${seen_fmt}|#{@ai_monitor_seen_${_seen_name}}"
|
|
927
|
+
done
|
|
928
|
+
|
|
929
|
+
while IFS='|' read -r pane_id pane_status _seen_flags; do
|
|
911
930
|
case "$pane_status" in
|
|
912
931
|
"!"|"?"|">")
|
|
913
932
|
local sanitized="${pane_id//[^a-zA-Z0-9]/_}"
|
|
@@ -923,13 +942,20 @@ _cleanup_stale_panes() {
|
|
|
923
942
|
fi
|
|
924
943
|
;;
|
|
925
944
|
"✓"|"-")
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
945
|
+
# 终态只认「进程存活」证据:seen=1 时保留(launcher/daemon 场景
|
|
946
|
+
# 进程树不可见);活跃态 !/?/> 的判据是归属证据,不使用 seen。
|
|
947
|
+
case "$_seen_flags" in
|
|
948
|
+
*1*) ;;
|
|
949
|
+
*)
|
|
950
|
+
if ! _pane_has_ai_process "$pane_id" "$ps_snap"; then
|
|
951
|
+
_ai_log "STALE: clearing terminal state '$pane_status' on $pane_id (no ai process)"
|
|
952
|
+
tmux set-option -pt "$pane_id" @ai_pane_status "" 2>/dev/null || true
|
|
953
|
+
fi
|
|
954
|
+
;;
|
|
955
|
+
esac
|
|
930
956
|
;;
|
|
931
957
|
esac
|
|
932
|
-
done < <(tmux list-panes -a -F "#{pane_id}|#{@ai_pane_status}" 2>/dev/null)
|
|
958
|
+
done < <(tmux list-panes -a -F "#{pane_id}|#{@ai_pane_status}${seen_fmt}" 2>/dev/null)
|
|
933
959
|
|
|
934
960
|
# 清理孤儿目录:目录存在但对应 pane 已不在 tmux 中
|
|
935
961
|
local all_pane_ids
|
|
@@ -1039,3 +1065,47 @@ _maybe_repair_hooks() {
|
|
|
1039
1065
|
fi
|
|
1040
1066
|
fi
|
|
1041
1067
|
}
|
|
1068
|
+
|
|
1069
|
+
# 跨工具自修复:tmux 入口所有 _refresh/_session-switched 固定传 TOOL_ID=claude,
|
|
1070
|
+
# codex/opencode 只有自身 SessionStart 能触发修复;hooks 文件被整体覆盖/删除后
|
|
1071
|
+
# 再无事件 → 永不自愈。本函数由 _run_refresh 调用,遍历其余 adapter,对
|
|
1072
|
+
# 「用户显式装过」(installed-<tool> 标记)的工具做 check → 缺失则 install。
|
|
1073
|
+
#
|
|
1074
|
+
# 不 source adapter:ADAPTER_* 与 adapter_* 会覆盖当前已加载的 adapter(TOOL_ID
|
|
1075
|
+
# 污染),一律子进程 exec wrapper(wrapper 自带 _LIB_DIR 与 adapter 加载)。
|
|
1076
|
+
# 未登记的工具只做「既存安装登记」:check 通过才补写标记,绝不自动安装——
|
|
1077
|
+
# 尊重 codex/opencode 非人人安装的现状。
|
|
1078
|
+
# 成本:每工具每 60s 至多 1 次 check;installed 存在且缺失时再加 1 次 install。
|
|
1079
|
+
_maybe_repair_other_tools() {
|
|
1080
|
+
local current="${TOOL_ID:-claude}"
|
|
1081
|
+
local f tool installer marker now last
|
|
1082
|
+
for f in "${_ADAPTER_DIR}"/*.sh; do
|
|
1083
|
+
[ -f "$f" ] || continue
|
|
1084
|
+
tool=$(basename "$f" .sh)
|
|
1085
|
+
[ "$tool" = "$current" ] && continue
|
|
1086
|
+
[ -f "${_SUPPRESS_DIR}/suppressed-${tool}" ] && continue
|
|
1087
|
+
installer="${_LIB_DIR}/install-${tool}-hooks.sh"
|
|
1088
|
+
[ -x "$installer" ] || continue
|
|
1089
|
+
marker="${_STATUS_DIR}/.hooks-repair-${tool}-ts"
|
|
1090
|
+
now=$(date +%s)
|
|
1091
|
+
[ -d "$_STATUS_DIR" ] || mkdir -p "$_STATUS_DIR" 2>/dev/null
|
|
1092
|
+
if [ -f "$marker" ]; then
|
|
1093
|
+
last=$(cat "$marker" 2>/dev/null)
|
|
1094
|
+
[ $((now - ${last:-0})) -lt 60 ] && continue
|
|
1095
|
+
fi
|
|
1096
|
+
echo "$now" > "$marker" 2>/dev/null || true
|
|
1097
|
+
if [ -f "$(_installed_marker_path "$tool")" ]; then
|
|
1098
|
+
"$installer" check >/dev/null 2>&1 && continue
|
|
1099
|
+
_ai_log "REPAIR: ${tool} hooks missing, reinstalling (cross-tool)"
|
|
1100
|
+
if "$installer" >/dev/null 2>&1; then
|
|
1101
|
+
tmux display-message "✓ ${tool} hooks 已自动修复(检测到缺失)" 2>/dev/null || true
|
|
1102
|
+
else
|
|
1103
|
+
tmux display-message "⚠ ${tool} hooks 自动修复失败,请运行 tmuxclihook 检查" 2>/dev/null || true
|
|
1104
|
+
fi
|
|
1105
|
+
elif "$installer" check >/dev/null 2>&1; then
|
|
1106
|
+
# 未登记但健康:登记既存安装(老用户升级迁移),绝不安装
|
|
1107
|
+
_installed_marker_write "$tool" 2>/dev/null || true
|
|
1108
|
+
_ai_log "REPAIR: ${tool} hooks healthy, registered installed marker"
|
|
1109
|
+
fi
|
|
1110
|
+
done
|
|
1111
|
+
}
|
package/scripts/tmux-ai-status
CHANGED
|
@@ -108,6 +108,9 @@ _notification_implicit_status() {
|
|
|
108
108
|
# 重建聚合状态(_refresh / _entered 共用)
|
|
109
109
|
_run_refresh() {
|
|
110
110
|
_maybe_repair_hooks
|
|
111
|
+
# 跨工具修复:_refresh 固定以 claude 身份运行,codex/opencode 的整体丢失
|
|
112
|
+
# 只能靠这里兜底(仅对 installed-<tool> 已登记的工具)。
|
|
113
|
+
_maybe_repair_other_tools
|
|
111
114
|
# 节流版清理(60s):tmux 生命周期 hook(pane-focus-in 等)高频触发,
|
|
112
115
|
# 全价 _cleanup_stale_panes 实测 ~455ms;卡死状态清理有 :37 的事件路径
|
|
113
116
|
# 节流兜底,此处无需每次全价。Stop/SessionEnd fallback 仍走全价精准清理。
|
package/src/check-detail.js
CHANGED
|
@@ -32,6 +32,9 @@ function baseDetail(tool) {
|
|
|
32
32
|
events: {},
|
|
33
33
|
plugin: null,
|
|
34
34
|
registeredPaths: [],
|
|
35
|
+
// codex 授信维度:{configFile, trusted: true|false|null, missingEvents, instances}
|
|
36
|
+
// 仅 codex adapter 提供;null = 该工具无此概念/旧版 adapter。
|
|
37
|
+
trust: null,
|
|
35
38
|
drift: null,
|
|
36
39
|
error: null,
|
|
37
40
|
raw: null,
|
|
@@ -70,6 +73,7 @@ export async function checkToolDetail(tool, { run } = {}) {
|
|
|
70
73
|
events: parsed.events || {},
|
|
71
74
|
plugin: parsed.plugin ?? null,
|
|
72
75
|
registeredPaths: parsed.registeredPaths || [],
|
|
76
|
+
trust: parsed.trust ?? null,
|
|
73
77
|
drift: parsed.drift || null,
|
|
74
78
|
error: parsed.error,
|
|
75
79
|
raw: stdout,
|
|
@@ -85,6 +89,7 @@ export async function checkToolDetail(tool, { run } = {}) {
|
|
|
85
89
|
events: parsed.events || {},
|
|
86
90
|
plugin: parsed.plugin ?? null,
|
|
87
91
|
registeredPaths: parsed.registeredPaths || [],
|
|
92
|
+
trust: parsed.trust ?? null,
|
|
88
93
|
drift: parsed.drift || null,
|
|
89
94
|
raw: stdout,
|
|
90
95
|
};
|
|
@@ -107,6 +112,32 @@ export function renderToolDetail(detail) {
|
|
|
107
112
|
lines.push(` ${WARN} 存在卸载意图标记(suppressed-${detail.tool}):自动修复已被抑制`);
|
|
108
113
|
}
|
|
109
114
|
|
|
115
|
+
// codex 授信维度:hooks 注册完整 ≠ 会执行。键缺失 = 一定不执行;键存在只说明
|
|
116
|
+
// 有授信记录(bash 无法复现 trusted_hash,改写后仍可能需重新 Trust)。
|
|
117
|
+
const trust = detail.trust;
|
|
118
|
+
if (trust) {
|
|
119
|
+
if (trust.trusted === false) {
|
|
120
|
+
const miss = (trust.missingEvents || []).join(', ') || '全部事件';
|
|
121
|
+
lines.push(
|
|
122
|
+
` ${WARN} hooks 未授信:当前实例缺少 [hooks.state] 记录(${miss})——codex 会跳过这些 hook,请在 codex TUI 选择 "Trust all and continue"`
|
|
123
|
+
);
|
|
124
|
+
} else if (trust.trusted === true) {
|
|
125
|
+
lines.push(` ${OK} 授信记录存在(键存在不等于 hash 匹配:hooks 改写后仍可能需重新 Trust)`);
|
|
126
|
+
} else if (detail.status === 'ok') {
|
|
127
|
+
lines.push(` ${WARN} 无法判定授信状态:${trust.configFile} 不存在`);
|
|
128
|
+
}
|
|
129
|
+
const otherUntrusted = (trust.instances || []).filter(
|
|
130
|
+
(i) => i.configFile !== trust.configFile && i.trusted === false
|
|
131
|
+
);
|
|
132
|
+
if (otherUntrusted.length > 0) {
|
|
133
|
+
lines.push(` ${WARN} 其他实例未授信(首次启动会弹 Hooks need review):`);
|
|
134
|
+
for (const i of otherUntrusted) {
|
|
135
|
+
const miss = (i.missingEvents || []).join(', ') || '全部事件';
|
|
136
|
+
lines.push(` ${i.configFile}(缺 ${miss})`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
110
141
|
// 逐事件缺失明细(cc-switch/文件模式的 tools)
|
|
111
142
|
if (detail.status === 'missing') {
|
|
112
143
|
const broken = [];
|
|
@@ -85,7 +85,8 @@ tmux bind-key r run-shell "'${CURRENT_DIR}/scripts/tmux-ai-reload'" 2>/dev/null
|
|
|
85
85
|
# #{hook_client_name} 这个 format 变量——误用它恒展开为空,handler 的
|
|
86
86
|
# [ -n "$3" ] 守卫静默跳过,entered_time 从不写入,排序退化为 created 序。
|
|
87
87
|
tmux set-hook -g client-session-changed "run-shell '${CURRENT_DIR}/scripts/tmux-ai-status claude _session-switched #{hook_client}'" 2>/dev/null || true
|
|
88
|
-
# 注:_refresh 与 TOOL_ID
|
|
88
|
+
# 注:_refresh 与 TOOL_ID 无关(重建聚合 + 自修复当前工具 hooks + 跨工具修复
|
|
89
|
+
# 已显式安装的 codex/opencode),统一传 claude。
|
|
89
90
|
tmux set-hook -g session-closed "run-shell '${CURRENT_DIR}/scripts/tmux-ai-status claude _refresh'"
|
|
90
91
|
tmux set-hook -g client-detached "run-shell '${CURRENT_DIR}/scripts/tmux-ai-status claude _refresh'"
|
|
91
92
|
# (client-attached 不再注册:attach 场景由 client-session-changed 覆盖)
|