ClaudeMods
☰
ZH-CN
● 0 人在线 · 浏览 0 次
赞助提交作品
GitHub 仓库 · 发布者 hughescr

mods-probe

一个 Claude Code hooks-module 标志探针,会记录每个工作阶段的 hooks 模块是否开启,并校准推断出的关闭状态。

hughescr@hughescr

hughescr/claude-code-config/tree/develop/my-plugins/mods-probe

已翻译

关于这个 mod

mods-probe

这是一个用于跟踪 Claude Code hooks 模块(“mods”)标志的探针。只有当服务端 GrowthBook 标志 tengu_plugin_hooks_modules 对该进程为 ON 时,mods 才会加载;关闭时,所有 hooks 模块都会被静默跳过。 这个插件把这种沉默变成证据。

在 session.start 时,它的模块会做两件事:

  1. 写入一行转录:mods-probe: hooks modules are ON in this process (logged to ~/.claude/mods-flag.log)。 这里选择转录行($.ui.log)而不是 toast:toast 会持续 4 s,启动时可能错过或尚未绘制;转录行会保留, 而 -p、SDK 和桌面主机也会通过 ui_log 收到它。
  2. 向 ~/.claude/mods-flag.log 追加一行:
   2026-10-01T12:34:56.789Z session=<id|?> surface=<terminal|desktop|vscode|mobile|none> interactive=<true|false> version=<claude version|?> on

对于启动时还没有任何绘制位置的主机(某些 SDK/桌面工作阶段),surface=none 是正常情况。

读取日志

标志关闭时模块从不运行,因此探针本身只能说明“开启”:没有它的记录,表示关闭或未知。“关闭”这一半来自配套的 SessionStart command hook:位于 ~/.claude 仓库中的 hooks/mods-flag-reconcile.ts(已接到 ~/.claude/settings.json,用 /opt/homebrew/bin/bun 运行)。设置钩子不受该标志控制,因此每个进程都会运行,并追加到同一个日志:

2026-10-01T12:34:56.000Z session=<id> source=<startup|resume|fork> pid=<claude pid|?> start
2026-10-01T12:34:56.789Z session=<id> surface=terminal interactive=true version=2.1.287 on        # this plugin
2026-10-01T12:38:10.000Z session=<id> pid=<claude pid|?> for_start=2026-10-01T12:34:56.000Z inferred off

三种记录使用同一种语法:<ISO time> key=value... <state>,最后一个 token 是状态(start、on、off),并且必须有 session=。规则如下:

  • 每个新的 claude 进程会写入一个 start。/clear 和 compact 不会结束进程,探针的 session.start 也不会为它们触发,因此不会写入任何内容。恢复工作阶段只有在新进程启动时才会写入 start(即自启动以来尚未有 start 的 pid)。
  • 某个 start 若已超过 2 分钟,而从它之前 30 s 到之后 5 分钟的范围内,同一工作阶段没有 on,那么下一个进程启动时会为它写入一行 inferred off。这行也是防止它再次被推断的标记。探针无法读取 session id(session=?)的工作阶段所写的 on,会计入它附近的任何 start。
  • 因此,on = mods 已开启;inferred off = 进程已启动但从未记录 on。不足 2 分钟的 start 仍在等待。

报告(只读;也会统计已经该写入 off 但尚未标记的 start;日期按 UTC 计算,off 计入它的 start 所在日):

bash
bun ~/.claude/hooks/mods-flag-reconcile.ts --report
# day         starts    on   off
# 2026-10-01       6     4     2
# total            6     4     2
# latest: off at 2026-10-01T12:34:56.000Z
# last transition: on -> off at 2026-10-01T12:34:56.000Z

原始视图(只有 on/off 行计数;start 行是分母):

bash
tail -n 20 ~/.claude/mods-flag.log
grep ' on$' ~/.claude/mods-flag.log | cut -dT -f1 | sort | uniq -c    # on-sessions per day
grep ' off$' ~/.claude/mods-flag.log | cut -dT -f1 | sort | uniq -c   # inferred offs per day (by inference time)

限制

模块在工作阶段运行时重新加载(热重载、启用)会再次触发 session.start,因此一个工作阶段偶尔会记录两次 on。每次 on 时,探针会通过读-改-写将文件裁剪到最新的 2000 行,因此钩子恰好在此刻追加的 start 可能丢失(没有问题:丢失的 start 永远不会产生 off)。两个探针在同一时刻写入时,可能会丢失一个 on,这会显示成错误的 off。 除非只是通过原子重命名裁剪过大的文件(超过 1 MiB,而且只在 mods 关闭足够久、探针没有进行裁剪时),钩子不会重写文件。如果探针报告的恢复工作阶段 id 与钩子看到的不一致,恢复的进程可能看起来像 off。如果找不到 claude pid(沙盒化的 ps),会记为 pid=?。

用以下命令交叉检查缓存的标志值: jq '.cachedGrowthBookFeatures.tengu_plugin_hooks_modules' ~/.claude.json。

加载方式

它从 craigs-claude-plugins 市集安装,并在 ~/.claude/settings.json 的 enabledPlugins 中启用。该设置覆盖这台机器上任何目录中的每个工作阶段:CLI(无论是否使用 claude-smart.sh)和共享用户设置的桌面应用都适用。claude-smart.sh --plugin-dir 不会使用:它只按项目类型加载插件,从不到达桌面应用。

编辑模块后,先在 .claude-plugin/plugin.json 中增加 version,然后执行 claude plugin marketplace update craigs-claude-plugins && claude plugin update mods-probe@craigs-claude-plugins 并重启工作阶段。另请参阅 my-plugins/MODS-ACTIVATION.md。

检查

bash
claude plugin validate my-plugins/mods-probe
DISABLE_GROWTHBOOK=1 claude plugin test my-plugins/mods-probe   # one-off only: never put this in a launcher or settings

安装

请先查看作者 README 确认 marketplace 和插件名称;命令可能随仓库结构改变。

claude plugin marketplace add hughescr/claude-code-config
claude plugin install mods-probe
原文 / README

mods-probe

A flag tracker for Claude Code hooks modules ("mods"). Mods load only while the server-side GrowthBook flag tengu_plugin_hooks_modules is ON for the process; while it is off, every hooks module is silently skipped. This plugin turns that silence into evidence.

On session.start its module does two things:

  1. Writes one transcript line: mods-probe: hooks modules are ON in this process (logged to ~/.claude/mods-flag.log). A transcript line ($.ui.log) was chosen over a toast: a toast lasts 4 s and can be missed or not yet drawn at launch, while the line persists, and -p/SDK/desktop hosts receive it as ui_log.

  2. Appends one line to ~/.claude/mods-flag.log:

    2026-10-01T12:34:56.789Z session=<id|?> surface=<terminal|desktop|vscode|mobile|none> interactive=<true|false> version=<claude version|?> on
    

    surface=none is normal for a host that draws nowhere yet at start (some SDK/desktop sessions).

Reading the log

The module never runs while the flag is off, so the probe alone can only say "on": silence from it means off or unknown. The "off" half comes from a companion SessionStart command hook, hooks/mods-flag-reconcile.ts in the ~/.claude repo (wired in ~/.claude/settings.json, run with /opt/homebrew/bin/bun). Settings hooks are not gated by the flag, so it runs in every process and appends to the same log:

2026-10-01T12:34:56.000Z session=<id> source=<startup|resume|fork> pid=<claude pid|?> start
2026-10-01T12:34:56.789Z session=<id> surface=terminal interactive=true version=2.1.287 on        # this plugin
2026-10-01T12:38:10.000Z session=<id> pid=<claude pid|?> for_start=2026-10-01T12:34:56.000Z inferred off

One grammar for all three: <ISO time> key=value... <state>, the last token is the state (start, on, off), session= is required. The rules:

  • Each new claude process writes one start. /clear and compact keep the process and the probe's session.start does not fire for them, so they write nothing. A resume writes a start only for a new process (a pid with no start since it launched).
  • A start older than 2 minutes with no on for the same session from 30 s before to 5 minutes after it gets one inferred off line on the next session start in any process. That line is also the marker that stops it being inferred again. The on of a session whose id the probe could not read (session=?) counts for any start near it.
  • So on = mods were on; inferred off = the process started and never logged on. A start younger than 2 minutes is still pending.

Report (read-only, also counts starts that are due an off but not yet marked; days are UTC, an off counts on its start's day):

bun ~/.claude/hooks/mods-flag-reconcile.ts --report
# day         starts    on   off
# 2026-10-01       6     4     2
# total            6     4     2
# latest: off at 2026-10-01T12:34:56.000Z
# last transition: on -> off at 2026-10-01T12:34:56.000Z

Raw views (only on/off lines count; start lines are the denominator):

tail -n 20 ~/.claude/mods-flag.log
grep ' on$' ~/.claude/mods-flag.log | cut -dT -f1 | sort | uniq -c    # on-sessions per day
grep ' off$' ~/.claude/mods-flag.log | cut -dT -f1 | sort | uniq -c   # inferred offs per day (by inference time)

Limits. A reload of the module while a session runs (hot reload, enable) raises session.start again, so a session can occasionally log on twice. The probe trims the file to its newest 2000 lines on each on, by read-modify-write, so a start the hook appended during that instant can be lost (harmless: a lost start never produces an off). Two probes writing in the same instant can lose an on, which would show as a false off. The hook never rewrites the file except to trim an oversized one (over 1 MiB, only when mods have been off long enough for the probe not to trim), by an atomic rename. If the probe does not report the resumed session's id the way the hook sees it, a resumed process can look like an off. A claude pid that cannot be found (a sandboxed ps) is logged as pid=?.

Cross-check the cached flag value with jq '.cachedGrowthBookFeatures.tengu_plugin_hooks_modules' ~/.claude.json.

How it loads

Installed from the craigs-claude-plugins marketplace and enabled in ~/.claude/settings.json (enabledPlugins), which covers every session on this machine in any directory: the CLI (with or without claude-smart.sh) and the desktop app, which share user settings. claude-smart.sh --plugin-dir is not used: it only loads plugins by project type and never reaches the desktop app.

After editing the module, bump version in .claude-plugin/plugin.json, then claude plugin marketplace update craigs-claude-plugins && claude plugin update mods-probe@craigs-claude-plugins and restart sessions. See my-plugins/MODS-ACTIVATION.md.

Check it

claude plugin validate my-plugins/mods-probe
DISABLE_GROWTHBOOK=1 claude plugin test my-plugins/mods-probe   # one-off only: never put this in a launcher or settings

更多类似作品