tmuxclihook 0.3.3 → 0.3.5
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/claude.sh +35 -15
- package/scripts/adapters/codex.sh +36 -6
- package/scripts/adapters/opencode.sh +13 -2
- package/scripts/install-claude-hooks.sh +28 -13
- package/scripts/install-codex-hooks.sh +22 -7
- package/scripts/install-opencode-hooks.sh +22 -7
- package/scripts/lib-install-hooks.sh +13 -0
- package/scripts/lib-tmux-ai-status.sh +6 -0
- package/scripts/tmux-ai-status +6 -2
- package/src/adapters-meta.js +6 -0
- package/src/detect.js +4 -3
- package/src/update.js +22 -17
- package/tmux-ai-hooks-status.tmux +3 -1
package/package.json
CHANGED
|
@@ -72,10 +72,7 @@ _CLAUDE_JQ_MERGE='
|
|
|
72
72
|
.hooks[]
|
|
73
73
|
| select(.command | contains("tmux-powerline-claude-status") | not)
|
|
74
74
|
| select(.command | contains("tmux-claude-status") | not)
|
|
75
|
-
| select(
|
|
76
|
-
(.command | contains("tmux-ai-status") | not)
|
|
77
|
-
or (.command | startswith($dev_script))
|
|
78
|
-
)
|
|
75
|
+
| select(.command | contains("tmux-ai-status") | not)
|
|
79
76
|
]
|
|
80
77
|
| select(.hooks | length > 0)
|
|
81
78
|
)
|
|
@@ -98,7 +95,7 @@ _CLAUDE_JQ_STRIP='
|
|
|
98
95
|
.hooks |= if . then
|
|
99
96
|
[. | to_entries[] |
|
|
100
97
|
.value |= [
|
|
101
|
-
.[] | .hooks = [.hooks[] | select(.command |
|
|
98
|
+
.[] | .hooks = [.hooks[] | select(.command | contains("tmux-ai-status") | not)]
|
|
102
99
|
| select(.hooks | length > 0)
|
|
103
100
|
]
|
|
104
101
|
| select(.value | length > 0)
|
|
@@ -107,13 +104,12 @@ _CLAUDE_JQ_STRIP='
|
|
|
107
104
|
'
|
|
108
105
|
|
|
109
106
|
_claude_apply_merge() { # $1=输入 JSON 文件 → stdout 变换后全文档
|
|
110
|
-
jq --arg
|
|
111
|
-
--arg tool_cmd "${_LIB_DIR}/tmux-ai-status claude" \
|
|
107
|
+
jq --arg tool_cmd "${_LIB_DIR}/tmux-ai-status claude" \
|
|
112
108
|
"$_CLAUDE_JQ_MERGE" "$1"
|
|
113
109
|
}
|
|
114
110
|
|
|
115
111
|
_claude_apply_strip() { # $1=输入 JSON 文件 → stdout 变换后全文档
|
|
116
|
-
jq
|
|
112
|
+
jq "$_CLAUDE_JQ_STRIP" "$1"
|
|
117
113
|
}
|
|
118
114
|
|
|
119
115
|
_claude_count_registered() { # $1=输入 JSON 文件 → stdout 事件数(jq 失败时为空)
|
|
@@ -187,18 +183,41 @@ ON CONFLICT(key) DO UPDATE SET value = json_patch(settings.value, json($(_sqlquo
|
|
|
187
183
|
}
|
|
188
184
|
|
|
189
185
|
# 完整性检查:cc-switch 模式查通用配置;否则查 hooks 配置文件。
|
|
190
|
-
# 10
|
|
186
|
+
# 三要素:10 事件计数 + 每个事件至少一条条目的脚本文件存活(test -x)。
|
|
187
|
+
# 计数齐但脚本死(npm 包卸载/volta 升级换路径,含部分事件死)→ missing → 自修复重装收敛。
|
|
188
|
+
# 存活提取内联在本函数(jq + test),不依赖 lib-install-hooks.sh——本函数同时被
|
|
189
|
+
# 事件路径(lib-tmux-ai-status.sh)与 install wrapper(lib-install-hooks.sh)两个上下文调用。
|
|
191
190
|
adapter_check_integrity() {
|
|
192
|
-
local registered
|
|
191
|
+
local src registered ev p ok_events=""
|
|
193
192
|
if _ccswitch_active; then
|
|
194
|
-
_ccswitch_dump_common
|
|
195
|
-
registered=$(_claude_count_registered "$tmp")
|
|
196
|
-
rm -f "$tmp"
|
|
193
|
+
_ccswitch_dump_common src || return 1
|
|
197
194
|
else
|
|
198
195
|
[ -f "$ADAPTER_HOOKS_FILE" ] || return 1
|
|
199
|
-
|
|
196
|
+
src="$ADAPTER_HOOKS_FILE"
|
|
197
|
+
fi
|
|
198
|
+
registered=$(_claude_count_registered "$src")
|
|
199
|
+
if [ "${registered:-0}" -ge 10 ]; then
|
|
200
|
+
while IFS=$'\t' read -r ev p; do
|
|
201
|
+
if [ -n "$ev" ] && [ -x "$p" ]; then
|
|
202
|
+
case ",$ok_events," in *",$ev,"*) ;; *) ok_events="$ok_events,$ev" ;; esac
|
|
203
|
+
fi
|
|
204
|
+
done < <(jq -r '.hooks as $h |
|
|
205
|
+
["SessionStart","SessionEnd","UserPromptSubmit","PreToolUse","PostToolUse",
|
|
206
|
+
"PostToolUseFailure","PermissionRequest","Notification","Stop","StopFailure"] as $events |
|
|
207
|
+
$events[] as $e |
|
|
208
|
+
[$h[$e][]?.hooks[]?.command // empty
|
|
209
|
+
| select(contains("tmux-ai-status")) | split(" ")[0]] as $scripts |
|
|
210
|
+
select(($scripts | length) > 0) |
|
|
211
|
+
$scripts[] | "\($e)\t\(.)"' "$src" 2>/dev/null)
|
|
200
212
|
fi
|
|
201
|
-
[ "$
|
|
213
|
+
if [ "$src" != "$ADAPTER_HOOKS_FILE" ]; then
|
|
214
|
+
rm -f "$src"
|
|
215
|
+
fi
|
|
216
|
+
[ "${registered:-0}" -ge 10 ] || return 1
|
|
217
|
+
for ev in SessionStart SessionEnd UserPromptSubmit PreToolUse PostToolUse PostToolUseFailure PermissionRequest Notification Stop StopFailure; do
|
|
218
|
+
case ",$ok_events," in *",$ev,"*) ;; *) return 1 ;; esac
|
|
219
|
+
done
|
|
220
|
+
return 0
|
|
202
221
|
}
|
|
203
222
|
|
|
204
223
|
# 安装:合并式更新,保留他人(masko 等)注册的 hook。cc-switch 模式只写库
|
|
@@ -233,6 +252,7 @@ adapter_uninstall_hooks() {
|
|
|
233
252
|
_install_require_jq
|
|
234
253
|
if _ccswitch_active; then
|
|
235
254
|
local tmp stripped cur_hooks final_hooks patch managed top_patch
|
|
255
|
+
_ccswitch_warn_gui
|
|
236
256
|
_ccswitch_dump_common tmp || return 1
|
|
237
257
|
cur_hooks=$(jq -c '.hooks // {}' "$tmp")
|
|
238
258
|
# 已无本插件条目 → no-op(否则会把保留的他人 hook 重写一遍)
|
|
@@ -36,14 +36,38 @@ ADAPTER_PERMISSION_PRE_TOOL_NAMES="request_permissions"
|
|
|
36
36
|
# 自修复/TUI 调用的安装脚本
|
|
37
37
|
ADAPTER_INSTALLER="${_LIB_DIR}/install-codex-hooks.sh"
|
|
38
38
|
|
|
39
|
-
#
|
|
39
|
+
# 完整性计数:6 个事件中注册了含 tmux-ai-status codex 的 command 的事件数(按事件,
|
|
40
|
+
# 非扁平总数——重复+缺失的组合在扁平计数下会假阳性)。
|
|
41
|
+
_CODEX_JQ_COUNT='
|
|
42
|
+
.hooks as $h |
|
|
43
|
+
["SessionStart","UserPromptSubmit","PreToolUse","PermissionRequest","PostToolUse","Stop"]
|
|
44
|
+
| map(select([$h[.][]?.hooks[]?.command // empty] | any(contains("tmux-ai-status codex"))))
|
|
45
|
+
| length
|
|
46
|
+
'
|
|
47
|
+
|
|
48
|
+
# 完整性检查:6 事件计数 + 每个事件至少一条条目的脚本文件存活(test -x)。
|
|
49
|
+
# 跨安装源路径无关(contains);脚本死路径(npm 包卸载/volta 换路径,含部分事件死)
|
|
50
|
+
# → missing → install 收敛(no-op 门禁随之放行,接受一次 re-trust 代价换功能恢复)。
|
|
40
51
|
adapter_check_integrity() {
|
|
41
52
|
[ -f "$ADAPTER_HOOKS_FILE" ] || return 1
|
|
42
|
-
local n
|
|
43
|
-
n=$(jq
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
local n ev p ok_events=""
|
|
54
|
+
n=$(jq "$_CODEX_JQ_COUNT" "$ADAPTER_HOOKS_FILE" 2>/dev/null)
|
|
55
|
+
[ "${n:-0}" -ge 6 ] || return 1
|
|
56
|
+
while IFS=$'\t' read -r ev p; do
|
|
57
|
+
if [ -n "$ev" ] && [ -x "$p" ]; then
|
|
58
|
+
case ",$ok_events," in *",$ev,"*) ;; *) ok_events="$ok_events,$ev" ;; esac
|
|
59
|
+
fi
|
|
60
|
+
done < <(jq -r '.hooks as $h |
|
|
61
|
+
["SessionStart","UserPromptSubmit","PreToolUse","PermissionRequest","PostToolUse","Stop"] as $events |
|
|
62
|
+
$events[] as $e |
|
|
63
|
+
[$h[$e][]?.hooks[]?.command // empty
|
|
64
|
+
| select(contains("tmux-ai-status codex")) | split(" ")[0]] as $scripts |
|
|
65
|
+
select(($scripts | length) > 0) |
|
|
66
|
+
$scripts[] | "\($e)\t\(.)"' "$ADAPTER_HOOKS_FILE" 2>/dev/null)
|
|
67
|
+
for ev in SessionStart UserPromptSubmit PreToolUse PermissionRequest PostToolUse Stop; do
|
|
68
|
+
case ",$ok_events," in *",$ev,"*) ;; *) return 1 ;; esac
|
|
69
|
+
done
|
|
70
|
+
return 0
|
|
47
71
|
}
|
|
48
72
|
|
|
49
73
|
# Codex 6 事件(全部同步——0.144 静默跳过 async=true 的 hook)
|
|
@@ -73,11 +97,17 @@ _codex_read_existing() {
|
|
|
73
97
|
}
|
|
74
98
|
|
|
75
99
|
# 安装:改写 hooks.json(剥离旧 managed group 后为每事件追加本插件 group),保留他人 hook。
|
|
100
|
+
# no-op if valid:事件齐全且脚本存活时不改写——重写即 trusted_hash 失效(command 路径
|
|
101
|
+
# 变化 → codex 启动要重新 Trust all),跨安装源重复安装因此保持既有授信。
|
|
76
102
|
# 清理旧 hooks.toml(0.144 不再读取)。trust 无法在 bash 复现,靠用户首启 codex TUI 授信。
|
|
77
103
|
adapter_install_hooks() {
|
|
78
104
|
_install_require_jq
|
|
79
105
|
mkdir -p "$(dirname "$ADAPTER_HOOKS_FILE")" 2>/dev/null || true
|
|
80
106
|
[ -f "$_CODEX_LEGACY_TOML" ] && rm -f "$_CODEX_LEGACY_TOML" && echo "移除旧 hooks.toml(0.144 不再读取)"
|
|
107
|
+
if adapter_check_integrity; then
|
|
108
|
+
echo "Codex hooks already installed and valid at $ADAPTER_HOOKS_FILE (no-op, trust preserved)"
|
|
109
|
+
return 0
|
|
110
|
+
fi
|
|
81
111
|
local hook_script="${_LIB_DIR}/tmux-ai-status"
|
|
82
112
|
local new_json
|
|
83
113
|
new_json=$(_codex_read_existing | _codex_strip_managed | jq \
|
|
@@ -39,11 +39,17 @@ ADAPTER_INSTALLER="${_LIB_DIR}/install-opencode-hooks.sh"
|
|
|
39
39
|
|
|
40
40
|
_OPENCODE_MARKER="tmux-ai-status (managed by tmux-ai-hooks-status)"
|
|
41
41
|
|
|
42
|
-
# 完整性检查:plugin
|
|
42
|
+
# 完整性检查:plugin 文件存在 + 本插件标记 + 结构指纹 + HOOK_SCRIPT 指向的脚本存活。
|
|
43
|
+
# 路径无关(不比对当前 _LIB_DIR——跨安装源的合法 plugin 同样有效);
|
|
44
|
+
# 脚本死路径(npm 包卸载/换路径)→ missing → 重装收敛。
|
|
43
45
|
adapter_check_integrity() {
|
|
44
46
|
[ -f "$ADAPTER_HOOKS_FILE" ] || return 1
|
|
45
47
|
grep -q "$_OPENCODE_MARKER" "$ADAPTER_HOOKS_FILE" 2>/dev/null || return 1
|
|
46
|
-
|
|
48
|
+
local script_path
|
|
49
|
+
script_path=$(sed -n 's/^const HOOK_SCRIPT = "\(.*\)"$/\1/p' "$ADAPTER_HOOKS_FILE" 2>/dev/null | head -n 1)
|
|
50
|
+
if [ -z "$script_path" ] || [ ! -x "$script_path" ]; then
|
|
51
|
+
return 1
|
|
52
|
+
fi
|
|
47
53
|
grep -q 'case "permission.asked"' "$ADAPTER_HOOKS_FILE" 2>/dev/null || return 1
|
|
48
54
|
grep -q 'case "question.asked"' "$ADAPTER_HOOKS_FILE" 2>/dev/null || return 1
|
|
49
55
|
grep -q 'case "permission.replied"' "$ADAPTER_HOOKS_FILE" 2>/dev/null || return 1
|
|
@@ -142,7 +148,12 @@ EOF
|
|
|
142
148
|
}
|
|
143
149
|
|
|
144
150
|
# 安装:写出/覆盖 plugin 文件。整文件替换(非合并)——同目录下他人 plugin 文件不受影响。
|
|
151
|
+
# no-op if valid:文件完整且 HOOK_SCRIPT 存活时不重写(避免跨安装源无谓覆盖)。
|
|
145
152
|
adapter_install_hooks() {
|
|
153
|
+
if adapter_check_integrity; then
|
|
154
|
+
echo "opencode plugin already installed and valid at $ADAPTER_HOOKS_FILE (no-op)"
|
|
155
|
+
return 0
|
|
156
|
+
fi
|
|
146
157
|
mkdir -p "$(dirname "$ADAPTER_HOOKS_FILE")" 2>/dev/null || true
|
|
147
158
|
local hook_script="${_LIB_DIR}/tmux-ai-status"
|
|
148
159
|
local content
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# install-claude-hooks.sh — Claude Code hooks 安装/卸载/检查 wrapper
|
|
3
|
-
# 用法: install-claude-hooks.sh [uninstall|check]
|
|
3
|
+
# 用法: install-claude-hooks.sh [install|uninstall|check|auto]
|
|
4
4
|
#
|
|
5
5
|
# 薄 wrapper:source 公共骨架 + claude adapter,dispatch 到 adapter_install_hooks /
|
|
6
6
|
# adapter_uninstall_hooks。实际逻辑在 scripts/adapters/claude.sh。
|
|
7
7
|
# check:输出 ok/missing(exit 0/1),供 TUI 侦测在 cc-switch 模式下走 bash 侧
|
|
8
8
|
# 完整性判定(sqlite 真源),与非 cc-switch 的文件判定共用同一真相。
|
|
9
|
+
# auto:tmux 入口加载时的自动安装——存在卸载意图标记(suppressed-claude)时不装。
|
|
10
|
+
# 显式 uninstall 成功写标记、显式 install 成功清标记(见 lib-install-hooks.sh)。
|
|
9
11
|
|
|
10
12
|
set -o errexit
|
|
11
13
|
set -o pipefail
|
|
@@ -14,15 +16,28 @@ _LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
|
14
16
|
source "${_LIB_DIR}/lib-install-hooks.sh"
|
|
15
17
|
source "${_LIB_DIR}/adapters/claude.sh"
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
fi
|
|
19
|
+
case "${1:-install}" in
|
|
20
|
+
uninstall)
|
|
21
|
+
adapter_uninstall_hooks
|
|
22
|
+
_hooks_suppress_write claude
|
|
23
|
+
;;
|
|
24
|
+
check)
|
|
25
|
+
if adapter_check_integrity; then
|
|
26
|
+
echo ok
|
|
27
|
+
else
|
|
28
|
+
echo missing
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
;;
|
|
32
|
+
auto)
|
|
33
|
+
if [ -f "$(_hooks_suppress_file claude)" ]; then
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
adapter_install_hooks
|
|
37
|
+
_hooks_suppress_clear claude
|
|
38
|
+
;;
|
|
39
|
+
*)
|
|
40
|
+
adapter_install_hooks
|
|
41
|
+
_hooks_suppress_clear claude
|
|
42
|
+
;;
|
|
43
|
+
esac
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# install-codex-hooks.sh — Codex CLI hooks
|
|
3
|
-
# 用法: install-codex-hooks.sh [uninstall]
|
|
2
|
+
# install-codex-hooks.sh — Codex CLI hooks 安装/卸载/检查 wrapper(需 codex ≥ 0.144)
|
|
3
|
+
# 用法: install-codex-hooks.sh [install|uninstall|check]
|
|
4
4
|
#
|
|
5
5
|
# 薄 wrapper:source 公共骨架 + codex adapter,dispatch 到 adapter_install_hooks /
|
|
6
6
|
# adapter_uninstall_hooks。实际逻辑在 scripts/adapters/codex.sh。
|
|
7
|
+
# check:输出 ok/missing(exit 0/1),供 TUI 侦测走 bash 侧完整性判定。
|
|
8
|
+
# 显式 uninstall 成功写标记、显式 install 成功清标记(见 lib-install-hooks.sh)。
|
|
7
9
|
|
|
8
10
|
set -o errexit
|
|
9
11
|
set -o pipefail
|
|
@@ -12,8 +14,21 @@ _LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
|
12
14
|
source "${_LIB_DIR}/lib-install-hooks.sh"
|
|
13
15
|
source "${_LIB_DIR}/adapters/codex.sh"
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
case "${1:-install}" in
|
|
18
|
+
uninstall)
|
|
19
|
+
adapter_uninstall_hooks
|
|
20
|
+
_hooks_suppress_write codex
|
|
21
|
+
;;
|
|
22
|
+
check)
|
|
23
|
+
if adapter_check_integrity; then
|
|
24
|
+
echo ok
|
|
25
|
+
else
|
|
26
|
+
echo missing
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
;;
|
|
30
|
+
*)
|
|
31
|
+
adapter_install_hooks
|
|
32
|
+
_hooks_suppress_clear codex
|
|
33
|
+
;;
|
|
34
|
+
esac
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# install-opencode-hooks.sh — opencode plugin
|
|
3
|
-
# 用法: install-opencode-hooks.sh [uninstall]
|
|
2
|
+
# install-opencode-hooks.sh — opencode plugin 安装/卸载/检查 wrapper
|
|
3
|
+
# 用法: install-opencode-hooks.sh [install|uninstall|check]
|
|
4
4
|
#
|
|
5
5
|
# 薄 wrapper:source 公共骨架 + opencode adapter,dispatch 到 adapter_install_hooks /
|
|
6
6
|
# adapter_uninstall_hooks。实际逻辑在 scripts/adapters/opencode.sh。
|
|
7
|
+
# check:输出 ok/missing(exit 0/1),供 TUI 侦测走 bash 侧完整性判定。
|
|
8
|
+
# 显式 uninstall 成功写标记、显式 install 成功清标记(见 lib-install-hooks.sh)。
|
|
7
9
|
|
|
8
10
|
set -o errexit
|
|
9
11
|
set -o pipefail
|
|
@@ -12,8 +14,21 @@ _LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
|
|
12
14
|
source "${_LIB_DIR}/lib-install-hooks.sh"
|
|
13
15
|
source "${_LIB_DIR}/adapters/opencode.sh"
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
case "${1:-install}" in
|
|
18
|
+
uninstall)
|
|
19
|
+
adapter_uninstall_hooks
|
|
20
|
+
_hooks_suppress_write opencode
|
|
21
|
+
;;
|
|
22
|
+
check)
|
|
23
|
+
if adapter_check_integrity; then
|
|
24
|
+
echo ok
|
|
25
|
+
else
|
|
26
|
+
echo missing
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
;;
|
|
30
|
+
*)
|
|
31
|
+
adapter_install_hooks
|
|
32
|
+
_hooks_suppress_clear opencode
|
|
33
|
+
;;
|
|
34
|
+
esac
|
|
@@ -24,3 +24,16 @@ _install_atomic_write() {
|
|
|
24
24
|
printf '%s\n' "$content" > "$tmp"
|
|
25
25
|
mv -f "$tmp" "$target"
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
# --- 卸载意图标记(suppression marker)---
|
|
29
|
+
# 显式 uninstall 成功 → 写 suppressed-<tool>;显式 install 成功 → 清。
|
|
30
|
+
# 自动修复路径(_maybe_repair_hooks / 入口 auto / SessionStart 重装)见标记一律不装。
|
|
31
|
+
# ⚠️ _SUPPRESS_DIR 与 lib-tmux-ai-status.sh 中的定义保持一致(两处同步改)。
|
|
32
|
+
_SUPPRESS_DIR="${TMUX_AI_SUPPRESS_DIR:-${XDG_CONFIG_HOME:-${HOME}/.config}/tmuxclihook}"
|
|
33
|
+
|
|
34
|
+
_hooks_suppress_file() { printf '%s/suppressed-%s' "$_SUPPRESS_DIR" "$1"; }
|
|
35
|
+
_hooks_suppress_write() {
|
|
36
|
+
mkdir -p "$_SUPPRESS_DIR" 2>/dev/null || true
|
|
37
|
+
date +%s > "$(_hooks_suppress_file "$1")" 2>/dev/null || true
|
|
38
|
+
}
|
|
39
|
+
_hooks_suppress_clear() { rm -f "$(_hooks_suppress_file "$1")" 2>/dev/null || true; }
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
7
|
|
|
8
8
|
_STATUS_DIR="${TMUX_AI_STATUS_DIR:-/tmp/ai-status}"
|
|
9
|
+
# 卸载意图标记目录(suppressed-<tool>)。⚠️ 与 lib-install-hooks.sh 中的定义保持一致(两处同步改)。
|
|
10
|
+
_SUPPRESS_DIR="${TMUX_AI_SUPPRESS_DIR:-${XDG_CONFIG_HOME:-${HOME}/.config}/tmuxclihook}"
|
|
9
11
|
STATUS_COLOR="#F1FA8C"
|
|
10
12
|
|
|
11
13
|
# --- 日志模块 ---
|
|
@@ -533,6 +535,10 @@ _check_hooks_integrity() {
|
|
|
533
535
|
# 不依赖 SESSION_ID/EVENT,可被 _refresh 触发。60s 节流。
|
|
534
536
|
# 安装器由 adapter 声明(ADAPTER_INSTALLER)。
|
|
535
537
|
_maybe_repair_hooks() {
|
|
538
|
+
# 用户显式卸载意图(suppressed-<tool>)→ 不自动修复,尊重到显式安装为止。
|
|
539
|
+
if [ -f "${_SUPPRESS_DIR}/suppressed-${TOOL_ID:-claude}" ]; then
|
|
540
|
+
return 0
|
|
541
|
+
fi
|
|
536
542
|
local marker="${_STATUS_DIR}/.hooks-repair-${TOOL_ID:-claude}-ts"
|
|
537
543
|
local now
|
|
538
544
|
now=$(date +%s)
|
package/scripts/tmux-ai-status
CHANGED
|
@@ -178,8 +178,12 @@ case "$EVENT" in
|
|
|
178
178
|
STATUS="-"
|
|
179
179
|
_clear_pending
|
|
180
180
|
if ! _check_hooks_integrity; then
|
|
181
|
-
"${
|
|
182
|
-
|
|
181
|
+
if [ -f "${_SUPPRESS_DIR}/suppressed-${TOOL_ID}" ]; then
|
|
182
|
+
_ai_log "REPAIR: suppressed by uninstall marker, skip (${TOOL_ID})"
|
|
183
|
+
else
|
|
184
|
+
"${ADAPTER_INSTALLER}" >/dev/null 2>&1
|
|
185
|
+
tmux display-message "⚠ ${TOOL_ID} hooks 已自动修复(检测到缺失)" 2>/dev/null || true
|
|
186
|
+
fi
|
|
183
187
|
fi
|
|
184
188
|
;;
|
|
185
189
|
SessionEnd)
|
package/src/adapters-meta.js
CHANGED
|
@@ -45,6 +45,9 @@ function buildTools(scriptsDir) {
|
|
|
45
45
|
installer: join(scriptsDir, 'install-codex-hooks.sh'),
|
|
46
46
|
hookMarker: 'tmux-ai-status codex',
|
|
47
47
|
minHookCount: 6,
|
|
48
|
+
// 无 cc-switch 真源(hooks.json 即文件真源),但统一走 wrapper check 以获得
|
|
49
|
+
// 脚本存活校验(死路径 → missing → repair 收敛),与 bash 侧同一份真相。
|
|
50
|
+
checkViaWrapper: true,
|
|
48
51
|
},
|
|
49
52
|
{
|
|
50
53
|
id: 'opencode',
|
|
@@ -62,6 +65,9 @@ function buildTools(scriptsDir) {
|
|
|
62
65
|
installer: join(scriptsDir, 'install-opencode-hooks.sh'),
|
|
63
66
|
hookMarker: 'tmux-ai-status (managed by tmux-ai-hooks-status)',
|
|
64
67
|
minHookCount: 1,
|
|
68
|
+
// 统一走 wrapper check:bash 侧含 HOOK_SCRIPT 存活校验(死路径 → missing →
|
|
69
|
+
// repair 收敛),JS 文件计数判不出死路径。
|
|
70
|
+
checkViaWrapper: true,
|
|
65
71
|
},
|
|
66
72
|
];
|
|
67
73
|
}
|
package/src/detect.js
CHANGED
|
@@ -121,10 +121,11 @@ function hooksInstalled(tool) {
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
// 异步判定:声明 checkViaWrapper
|
|
125
|
-
//
|
|
124
|
+
// 异步判定:声明 checkViaWrapper 时走 bash wrapper check(cc-switch 模式在 bash 侧
|
|
125
|
+
// 按 sqlite 通用配置判定真源;所有 adapter check 均含脚本存活校验);未声明的工具
|
|
126
|
+
// 回落本地文件计数。
|
|
126
127
|
async function hooksInstalledAsync(tool) {
|
|
127
|
-
if (tool.checkViaWrapper && tool.ccSwitchDb
|
|
128
|
+
if (tool.checkViaWrapper && (!tool.ccSwitchDb || existsSync(tool.ccSwitchDb))) {
|
|
128
129
|
try {
|
|
129
130
|
const { stdout, exitCode } = await execa('bash', [tool.installer, 'check'], {
|
|
130
131
|
reject: false,
|
package/src/update.js
CHANGED
|
@@ -12,8 +12,9 @@ export const CURRENT_VERSION = packageJson.version;
|
|
|
12
12
|
// `npm install -g` 在版本管理器(volta 等)环境下不会更新其管理的 shim:
|
|
13
13
|
// volta 的 `~/.volta/bin` shim 指向 packages/ 镜像,而 npm -g 只写 node
|
|
14
14
|
// 工具链目录,两者互不感知(实测 0.3.0 → 0.3.2 升级时用户命令仍解析到
|
|
15
|
-
//
|
|
16
|
-
//
|
|
15
|
+
// 旧版)。生效收敛由 repairWithVolta 无条件完成(见下);本节自检只是
|
|
16
|
+
// 事后确认:按 update 进程的 PATH 解析一份,尽力读取真实版本并与
|
|
17
|
+
// registry latest 比对,无法确认或版本不符时打警告。
|
|
17
18
|
// 验证是尽力而为的提示,不改变 update 的返回值(install 本身已成功)。
|
|
18
19
|
|
|
19
20
|
function findOnPath(name, env) {
|
|
@@ -95,9 +96,12 @@ function warnNotEffective(log, result) {
|
|
|
95
96
|
|
|
96
97
|
// volta 生效修正:`npm install -g` 只写 node 工具链目录,不更新 volta 的
|
|
97
98
|
// shim 镜像(实测 volta 2.0.2:shim npm 的全局 prefix 就是工具链目录),
|
|
98
|
-
// 用户的命令经 ~/.volta/bin shim
|
|
99
|
-
//
|
|
100
|
-
//
|
|
99
|
+
// 用户的命令经 ~/.volta/bin shim 解析时仍是旧版。因此只要检测到 volta
|
|
100
|
+
// 就无条件补一次 volta install 完成生效(幂等,已是最新时秒回)。不走
|
|
101
|
+
// 「自检失败才修」的条件路径:自检按 update 进程自己的 PATH 找第一份,
|
|
102
|
+
// 可能恰好命中 npm -g 刚写的那份而误判 ok(实测 0.3.3 → 0.3.4:verify
|
|
103
|
+
// 判 ok,修复从未触发,用户命令仍是旧版)。npm install -g 是安装语义,
|
|
104
|
+
// volta install 是生效语义,两者都执行;用户无需改变安装习惯。
|
|
101
105
|
export async function repairWithVolta(run, log, env = process.env) {
|
|
102
106
|
const voltaHome = env.VOLTA_HOME || join(env.HOME || '', '.volta');
|
|
103
107
|
const voltaBin = findOnPath('volta', env) || join(voltaHome, 'bin', 'volta');
|
|
@@ -149,19 +153,20 @@ export async function updatePackage(options = {}) {
|
|
|
149
153
|
|
|
150
154
|
log.log(`Done. Updated to v${latestVersion}`);
|
|
151
155
|
|
|
156
|
+
// 生效收敛:有 volta 就无条件跑(幂等;无 volta 时静默跳过,纯 npm 场景
|
|
157
|
+
// npm -g 本身已生效)。任何命令行执行 update 都能升级生效的关键。
|
|
158
|
+
if (repairVolta) {
|
|
159
|
+
await repairVolta(run, log);
|
|
160
|
+
}
|
|
161
|
+
|
|
152
162
|
if (verify) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (result.status === 'ok') {
|
|
161
|
-
log.log(`Verified: tmuxclihook on your PATH now resolves to v${latestVersion}.`);
|
|
162
|
-
} else {
|
|
163
|
-
warnNotEffective(log, result);
|
|
164
|
-
}
|
|
163
|
+
// 事后确认(尽力而为):两处落点都应已是新版,命中哪份都该 ok;
|
|
164
|
+
// 不 ok(如 PATH 上另有第三份旧安装)只警告,不影响返回值。
|
|
165
|
+
const result = await verify(latestVersion);
|
|
166
|
+
if (result.status === 'ok') {
|
|
167
|
+
log.log(`Verified: tmuxclihook on your PATH now resolves to v${latestVersion}.`);
|
|
168
|
+
} else {
|
|
169
|
+
warnNotEffective(log, result);
|
|
165
170
|
}
|
|
166
171
|
}
|
|
167
172
|
|
|
@@ -58,8 +58,10 @@ if [ -z "$(tmux show-option -gv @claude_hooks_reload_registered 2>/dev/null)" ];
|
|
|
58
58
|
fi
|
|
59
59
|
|
|
60
60
|
# --- 自动注册 Claude hooks(幂等,每次插件加载时确保 hooks 存在)---
|
|
61
|
+
# auto 模式:用户显式卸载过(suppressed-claude 标记在)时静默跳过,尊重卸载意图;
|
|
62
|
+
# 显式安装(prefix+C-h / TUI)才会清除标记恢复自动守护。
|
|
61
63
|
# 注:codex/opencode hooks 不自动安装(非人人安装),用 prefix+M-h / prefix+C-p 手动装。
|
|
62
|
-
"${CURRENT_DIR}/scripts/install-claude-hooks.sh" >/dev/null 2>&1 || true
|
|
64
|
+
"${CURRENT_DIR}/scripts/install-claude-hooks.sh" auto >/dev/null 2>&1 || true
|
|
63
65
|
|
|
64
66
|
# --- 启动 tmux server 级状态监控器 ---
|
|
65
67
|
# Codex/opencode 均无 SessionEnd(codex 的 SessionStart 还延迟到首个 turn);
|