关于 hookshot
hookshot 是用于 Claude Code 和 Codex 的技能,可从仓库文档生成 Agent 提醒和检查钩子。它默认只发出指导信息;交付门禁可先记录模拟结果,再由仓库选择是否启用阻止模式。
主要功能
- 01根据文档生成编辑提醒钩子
- 02警告已追踪技能的本地修改
- 03对 Markdown 文件运行 lint
- 04提供会话预检与交付门禁
- 05合并现有钩子且避免重复
适用场景
- 01修改关键代码前提示 Agent 阅读相关文档
- 02发现同步技能被编辑时提醒回馈上游
- 03编辑 Markdown 后显示 lint 警告
- 04根据结果决定是否阻止未满足交付约定的会话结束
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ fellowship-dev/ dogfooded-skills --skill hookshot安装后在 Claude Code 中输入/hookshot确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/contract-format.mdshadow-log.mdscripts/gate.pypreflight.pytest_gate.py详细介绍
Hookshot
从文档到强制执行钩子。读取你编写的文档,并生成让 Agent 阅读文档的钩子。
理念: Hookshot 默认发出警告,只有仓库主动选择启用的门禁才会阻止操作。它绝不会修改文件,也不会执行检查并自动修复。它生成的每个钩子都会提供指导并以状态码 0 退出;唯一的例外是交付门禁,仓库可以在查看自己的影子日志后,将其从影子模式(仅记录)切换为强制模式(拒绝停止)。
模式
Hookshot 可组合使用——可通过一个或多个标志调用。默认模式(不带标志)运行文档覆盖率生成器。
| 标志 | 添加的功能 |
|---|---|
| (无标志) | 文档覆盖率 — PreToolUse Edit/Write 钩子,在编辑受覆盖的文件前,提醒 Agent 阅读相关的 docs/ 部分。(默认行为,详见下文。) |
--drift-warn | 技能漂移警告 — PreToolUse Edit/Write 钩子,当 Agent 编辑 .claude/skills/<name>/ 中属于 skills-lock.json 所追踪技能的文件时发出警告。应在上游进行编辑。 |
--md-lint | Markdown 检查 — PostToolUse Edit/Write 钩子,对任何有改动的 *.md 文件运行 npx markdownlint-cli2,并显示警告。绝不自动修复。 |
--preflight | 会话预检 — SessionStart 钩子(启动、恢复、清除、压缩),告知 Agent 其会话 ID、为任务运行预检的要求,以及记录其结果契约指针的位置。附带 scripts/preflight.py。 |
--gate | 交付门禁 — Stop 钩子,将现有内容与会话的结果契约进行比对。影子模式会记录可能拒绝停止的情况;强制模式会拒绝停止。附带 scripts/gate.py、references/contract-format.md、references/shadow-log.md。 |
标志可以组合使用:/hookshot --drift-warn --md-lint 会在默认的文档覆盖率钩子之外,同时安装这两个新钩子。只有 --preflight 和 --gate 模式会附带自己的脚本,也只有这两种模式会同时安装到 Codex 和 Claude Code。再次运行 Hookshot 会以幂等方式合并现有钩子。
通过 npx 安装:
npx skills add fellowship-dev/dogfooded-skills/ops/hookshot
适用场景
- 创建知识层(
ARCHITECTURE.md、docs/)后——Hookshot 会将其接入 Agent 运行时 - 更新
docs/code-structure.md或docs/code-guidelines.md后——重新生成钩子以保持内容最新 - 出现
#1585-class bug时:Agent 未阅读文档便修改了关键路径——添加钩子以防止问题再次发生 - 添加新的 FlowChad 流程后——为该关键路径生成钩子
与 Pylot 集成
- 安装:
boot-skills.sh会通过npx skills add安装 Hookshot 技能。但安装技能 ≠ 生成钩子。你必须至少运行一次/hookshot,以生成相关产物(doc-coverage.json、check-docs.sh、settings.json钩子、docs/hooks.md)。 - 过时检查:
entropy-check会在 PR 合并时以及每周定时任务中监控 Hookshot 覆盖率是否为最新内容。发现内容过时后,重新运行/hookshot。 - 按仓库配置: 每个仓库都有自己的钩子。跨仓库任务使用目标仓库的钩子。
关键见解
“由于这些检查是自定义的,我们会编写错误消息,将修复指导注入 Agent 上下文。” ——OpenAI harness engineering
Hookshot 会自动完成这件事。它本可以告知 Agent“修改此区域前,先了解 check_redirect
的工作方式”——此技能会根据你的文档生成这条消息。
生成内容
check-docs.sh— 给定正在编辑的文件,如果该文件受docs/中的文档覆盖,则向 stderr 输出文档提醒.claude/settings.json钩子 — 在每次 Edit/Write 时调用check-docs.sh的 PreToolUse 钩子- 领域覆盖映射 —
$REPO_ROOT/.claude/doc-coverage.json— 将文件 glob 映射到文档部分 - 自定义检查消息 — 包含特定文档部分链接的修复指导
说明
0. 确定仓库
REPO_ROOT=$(git rev-parse --show-toplevel)
REPO_NAME=$(basename $(git remote get-url origin) .git)
mkdir -p $REPO_ROOT/.claude
1. 解析知识层
读取所有文档文件并提取覆盖映射:
# Read all key docs
cat $REPO_ROOT/docs/code-structure.md 2>/dev/null
cat $REPO_ROOT/docs/code-guidelines.md 2>/dev/null
cat $REPO_ROOT/ARCHITECTURE.md 2>/dev/null
ls $REPO_ROOT/.flowchad/flows/ 2>/dev/null
针对 docs/code-structure.md 中的每个领域部分,提取:
- 领域名称(章节标题)
- 目录路径(“Directory:” 行)
- 关键文件(来自 Entry Points 表格)
- 关键模式(“Don't Repeat”部分——优先级最高)
针对 .flowchad/flows/ 中的每个 FlowChad 流程:
- 流程名称
- 领域
- 入口点文件(来自流程定义)
- 涉及的文件(流程中所有
file:条目)
2. 构建覆盖映射
创建 $REPO_ROOT/.claude/doc-coverage.json:
{
"version": "1",
"generated": "{DATE}",
"repo": "{REPO_NAME}",
"entries": [
{
"glob": "app/controllers/**/*.rb",
"domain": "Controllers",
"doc_section": "docs/code-structure.md#controllers",
"reminder": "Before modifying a controller, read how the controller pattern works: docs/code-structure.md#controllers. Key rule: controllers do not query the DB directly — use service objects.",
"criticality": "high"
},
{
"glob": "app/services/**/*.rb",
"domain": "Services",
"doc_section": "docs/code-structure.md#services",
"reminder": "Service objects in app/services/ follow the Command pattern. Read docs/code-structure.md#services for the interface contract.",
"criticality": "medium"
}
]
}
为每个领域目录映射创建一个条目。对于关键路径(在 FlowChad 流程中发现的路径),将 criticality 设为 "high"。
提醒文本规则:
- 以要阅读的具体文档章节开头
- 包含该领域最重要的“Don't Repeat”规则
- 不超过 200 个字符——这会显示在 Agent 上下文中,而不是一大段文字
- 具有可操作性:“阅读 X”,而不是“可以考虑阅读 X”
3. 生成 check-docs.sh
写入 $REPO_ROOT/.claude/check-docs.sh:
#!/usr/bin/env bash
# check-docs.sh — Generated by hookshot on {DATE}
# Usage: check-docs.sh <file_path_being_edited>
# Outputs doc reminders to stderr if the file is covered by docs/
set -euo pipefail
FILE_PATH="${1:-}"
if [ -z "$FILE_PATH" ]; then
exit 0
fi
# Normalize path relative to repo root
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
COVERAGE_MAP="$SCRIPT_DIR/doc-coverage.json"
if [ ! -f "$COVERAGE_MAP" ]; then
exit 0
fi
# Check file against each glob in the coverage map
# Uses jq to parse coverage map and bash glob matching
REMINDERS=$(jq -r '.entries[] | "\(.glob)\t\(.reminder)\t\(.criticality)"' "$COVERAGE_MAP" 2>/dev/null)
FOUND_REMINDER=""
FOUND_CRITICALITY=""
while IFS=$'\t' read -r GLOB REMINDER CRITICALITY; do
# Normalize the file path
REL_PATH="${FILE_PATH#$REPO_ROOT/}"
# Normalize for bash [[ ]] pattern matching:
# 1. **/ → * (** has no special meaning; * already matches any char incl /)
# 2. Escape [ ] so Next.js routes like [locale] are literal, not char classes
GLOB="${GLOB//\*\*\//*}"
GLOB="${GLOB//\[/\\[}"
GLOB="${GLOB//\]/\\]}"
if [[ "$REL_PATH" == $GLOB ]]; then
FOUND_REMINDER="$REMINDER"
FOUND_CRITICALITY="$CRITICALITY"
break
fi
done <<< "$REMINDERS"
if [ -n "$FOUND_REMINDER" ]; then
if [ "$FOUND_CRITICALITY" = "high" ]; then
echo "⚠️ DOCUMENTATION REMINDER (high criticality)" >&2
echo "$FOUND_REMINDER" >&2
echo "" >&2
echo "This file is in a critical path. Read the doc section before proceeding." >&2
else
echo "📖 Doc reminder: $FOUND_REMINDER" >&2
fi
fi
exit 0
赋予其可执行权限:
chmod +x $REPO_ROOT/.claude/check-docs.sh
4. 将钩子写入 settings.json
如果 .claude/settings.json 已存在,先读取。
合并钩子配置——不要覆盖现有钩子。
要添加的钩子:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash {REPO_ROOT}/scripts/check-docs.sh \"$(jq -r '.tool_input.file_path // empty')\""
}
]
}
]
}
}
合并策略:
- 如果
PreToolUse已存在 → 添加到数组中,不要替换 - 如果已存在完全相同的
check-docs.sh钩子 → 跳过(幂等) - 保留所有现有钩子条目
将合并后的结果写回 .claude/settings.json。
5. 生成自定义 Lint 消息
为 docs/code-guidelines.md 中的每条准则生成一个 lint 消息文件:
创建 $REPO_ROOT/.claude/lint-messages.md:
# Custom Lint Messages — {REPO_NAME}
Generated by hookshot on {DATE}. Used by PreToolUse hooks to inject remediation context.
## {Domain}: {Rule Name}
**Pattern detected:** {what triggers this message}
**Message injected into context:**
> {The exact message the agent will see}
**Doc reference:** docs/code-guidelines.md#{anchor}
---
{Repeat per rule}
对于关键规则(例如,“绝不自行实现身份验证”、“使用 check_redirect,而不是内联条件判断”),生成明确的检查命令并添加到 check-docs.sh:
# Add to check-docs.sh after the glob check:
# Rule-based checks (pattern detection in file content)
if echo "$FILE_PATH" | grep -q "controllers/"; then
# Check if file being written contains a raw redirect without check_redirect
# (This is a hint — actual content checking happens post-edit)
echo "📖 Controllers reminder: Use check_redirect in lib/redirect_service.rb for all redirects." >&2
fi
将这些基于规则的检查添加到 check-docs.sh 中,并放在清晰标注的部分。
5b. 生成 docs/hooks.md
在目标仓库中生成 docs/hooks.md 文件,说明当前启用的钩子:
# Hooks — {REPO_NAME}
> Auto-generated by [hookshot](https://github.com/fellowship-dev/dogfooded-skills). Safe to add notes — hookshot merges on update, it won't overwrite your additions.
## Active Hooks
### PreToolUse: Doc Reminders on Edit/Write
**Trigger:** Every `Edit` or `Write` tool call
**Script:** `scripts/check-docs.sh` (or `.claude/check-docs.sh`)
**Config:** `.claude/settings.json` → `hooks.PreToolUse`
**Coverage map:** `.claude/doc-coverage.json`
When an agent edits a file matching a covered glob, the hook injects a doc reminder into context before the edit proceeds. High-criticality files produce warnings; medium-criticality files produce reminders.
## Covered Domains
{For each entry in doc-coverage.json, list:}
| Domain | Glob | Criticality | Reminder |
|--------|------|-------------|----------|
| {domain} | `{glob}` | {criticality} | {reminder} |
## Maintaining Hooks
- **Quick tweaks:** Edit `.claude/doc-coverage.json` directly — add/remove entries, adjust criticality or reminder text. Changes take effect immediately.
- **Full regeneration:** Run `/hookshot` to rebuild coverage map from current `docs/code-structure.md`. This merges with your existing `doc-coverage.json` and `docs/hooks.md` — it won't overwrite manual additions.
- **Staleness detection:** `/entropy-check` monitors whether hooks are current vs docs. If it flags staleness, re-run `/hookshot`.
## Troubleshooting
### Glob doesn't match expected files
The hook uses bash `[[ ]]` pattern matching, which differs from gitignore globs:
- `**` has no special meaning — `*` already matches any character including `/`
- `[brackets]` are character classes, not literal — Next.js routes like `[locale]` need escaping
- The generated `check-docs.sh` normalizes both automatically. If you're writing manual globs, use `*` not `**/*` for recursive matching.
Test a glob: `bash scripts/check-docs.sh "/full/path/to/file.ts"`
### Hook breaks the agent or slows edits
Disable temporarily by removing the hook entry from `.claude/settings.json`. Re-run `/hookshot` to restore.
### settings.json got clobbered
Re-run `/hookshot` — it merges hooks into existing settings, never overwrites other config.
再次运行时,读取现有的 docs/hooks.md 并进行合并:保留人工添加的部分,并根据当前状态重新生成“Covered Domains”表格和“Active Hooks”部分。
6. 验证
测试生成的钩子:
# Test with a file that should trigger a reminder
bash $REPO_ROOT/.claude/check-docs.sh "$REPO_ROOT/app/controllers/sessions_controller.rb"
# Test with a file that should NOT trigger
bash $REPO_ROOT/.claude/check-docs.sh "$REPO_ROOT/README.md"
# Verify settings.json is valid JSON
cat $REPO_ROOT/.claude/settings.json | jq . > /dev/null && echo "settings.json: valid JSON"
# Verify doc-coverage.json is valid JSON
cat $REPO_ROOT/.claude/doc-coverage.json | jq . > /dev/null && echo "doc-coverage.json: valid JSON"
7. 汇总报告
## Hookshot Complete: {REPO_NAME}
### Coverage Map
- {N} domain entries in .claude/doc-coverage.json
- {N} high-criticality entries (will produce warnings)
- {N} medium-criticality entries (will produce reminders)
### Hooks Generated
- .claude/check-docs.sh — glob-based doc lookup
- .claude/settings.json — PreToolUse hook wired
- .claude/lint-messages.md — custom lint message catalog
- docs/hooks.md — human-readable hook documentation
### Coverage Gaps
{List any domains in ARCHITECTURE.md that have no glob coverage — need manual mapping}
### Manual Next Steps
- [ ] Review .claude/doc-coverage.json — adjust globs that are too broad or too narrow
- [ ] Test a real edit to a covered file and confirm the reminder appears
- [ ] Add rule-based checks for your most critical "Don't Repeat" patterns
- [ ] Run /entropy-check to verify grades reflect the new hook coverage
模式:漂移警告(--drift-warn)
当 Agent 即将编辑 .claude/skills/<name>/ 目录中的文件,且对应技能已在 skills-lock.json 中跟踪时发出警告。不会阻止实际编辑——这只是提示,Agent 有时确实需要先对同步的技能进行紧急修复,再提交上游。
生成 .claude/check-skill-drift.sh
#!/usr/bin/env bash
# check-skill-drift.sh — Generated by hookshot (--drift-warn) on {DATE}
# Usage: check-skill-drift.sh <file_path>
# Warns to stderr if the file belongs to a skill tracked in skills-lock.json.
set -uo pipefail
FILE_PATH="${1:-}"
[ -z "$FILE_PATH" ] && exit 0
# Walk up from the file to find the nearest skills-lock.json
DIR="$(dirname "$FILE_PATH")"
LOCK_FILE=""
while [ "$DIR" != "/" ] && [ "$DIR" != "." ]; do
if [ -f "$DIR/skills-lock.json" ]; then
LOCK_FILE="$DIR/skills-lock.json"
break
fi
DIR="$(dirname "$DIR")"
done
[ -z "$LOCK_FILE" ] && exit 0
# Path must contain /.claude/skills/<name>/ or /.agents/skills/<name>/
SKILL_NAME="$(echo "$FILE_PATH" | sed -nE 's|.*/\.(claude|agents)/skills/([^/]+)/.*|\2|p')"
[ -z "$SKILL_NAME" ] && exit 0
# Look up in lockfile
SOURCE=$(python3 -c "
import json, sys
try:
data = json.load(open('$LOCK_FILE'))
entry = (data.get('skills') or {}).get('$SKILL_NAME')
if entry:
print(entry.get('source', ''))
except Exception:
pass
" 2>/dev/null)
if [ -n "$SOURCE" ]; then
echo "⚠️ SKILL DRIFT WARNING" >&2
echo "'$SKILL_NAME' is a remote skill synced from: $SOURCE" >&2
echo "Local edits will drift from upstream and may be overwritten on next 'npx skills update'." >&2
echo "Edit upstream at https://github.com/$SOURCE instead, or be prepared to PR the change back." >&2
fi
exit 0
赋予其可执行权限,并使用匹配器 Edit|Write 将其接入 .claude/settings.json 中的 PreToolUse。采用与默认文档覆盖钩子相同的合并策略,不要覆盖现有配置。
验证
# Should warn — entropy-check is a tracked skill
bash .claude/check-skill-drift.sh "$PWD/.claude/skills/entropy-check/SKILL.md"
# Should be silent — file is outside any skills dir
bash .claude/check-skill-drift.sh "$PWD/README.md"
# Should be silent — skill isn't in lockfile (e.g. a local-only skill)
bash .claude/check-skill-drift.sh "$PWD/.claude/skills/local-thing/SKILL.md"
模式:Markdown Lint(--md-lint)
在 Edit 或 Write 后,对任何已更改的 .md 文件运行 npx markdownlint-cli2,并将警告反馈给 Agent。绝不自动修复——由 Agent 决定。
起始版 .markdownlint.json
如果仓库根目录没有 .markdownlint.json 或 .markdownlint-cli2.jsonc,则添加一个宽松的起始配置,避免一开始就被 lint 规则淹没:
{
"default": true,
"MD013": false,
"MD033": false,
"MD041": false
}
MD013(行长度)——默认关闭;文档和技能文件经常包含长行MD033(内联 HTML)——关闭;我们在报告中使用 HTML details/summaryMD041(第一行必须是 h1)——关闭;许多文档以 frontmatter 开头
如果已有配置,则保持不变。绝不覆盖。
生成 .claude/check-md-lint.sh
#!/usr/bin/env bash
# check-md-lint.sh — Generated by hookshot (--md-lint) on {DATE}
# Usage: check-md-lint.sh <file_path>
# Runs markdownlint-cli2 on the file if it's *.md. Warns only — never fixes.
set -uo pipefail
FILE_PATH="${1:-}"
[ -z "$FILE_PATH" ] && exit 0
# Only lint markdown files
case "$FILE_PATH" in
*.md|*.markdown) ;;
*) exit 0 ;;
esac
[ -f "$FILE_PATH" ] || exit 0
# Run markdownlint-cli2 — fast start via npx
OUTPUT=$(npx --yes markdownlint-cli2 "$FILE_PATH" 2>&1) || true
if [ -n "$OUTPUT" ] && echo "$OUTPUT" | grep -qE 'MD[0-9]{3}'; then
echo "📝 Markdown lint warnings for $(basename "$FILE_PATH"):" >&2
echo "$OUTPUT" | grep -E 'MD[0-9]{3}' | head -20 >&2
echo "(warnings only — no auto-fix. Run 'npx markdownlint-cli2 --fix <file>' manually if desired.)" >&2
fi
exit 0
使用匹配器 Edit|Write 将其接入 .claude/settings.json 中的 PostToolUse(而不是 PreToolUse——文件必须先存在,才能运行 lint)。
验证
# Should print MD### warnings if the file has any lint issues
bash .claude/check-md-lint.sh README.md
# Should be silent — not a markdown file
bash .claude/check-md-lint.sh package.json
模式:会话预检与交付门禁(--preflight、--gate)
两个钩子,由一个主体维护。脚本位于此技能中(scripts/preflight.py、scripts/gate.py,使用 Python 3 标准库),客户端配置只负责指向这些脚本。仓库不会保存逻辑副本,只保存接线配置。
- 预检(启动、恢复、清除、压缩时触发 SessionStart):注入会话 ID,提醒 Agent 在首条提示词是任务时运行预检,并提供指针路径
<state>/contracts/<session_id>,供 Agent 记录其契约相对于仓库的路径。创建<state>/contracts/。 - 门禁(触发 Stop):通过该指针查找契约,按
references/contract-format.md检查契约,按references/shadow-log.md向影子日志追加一行,并在强制模式下返回{"decision": "block", "reason": …},列出未满足的项目。stop_hook_active为 true 时,绝不会阻止流程;发生任何内部错误时,均以状态码 0 退出。
两个客户端都会通过标准输入传入 session_id、cwd、source(SessionStart)以及 stop_hook_active、last_assistant_message(Stop),并接受相同的 additionalContext 和 decision: block 结构。Codex 会解析并跳过 prompt/agent 处理程序,因此只有 Claude Code 原生支持大模型评审;在 Codex 上,门禁会记录 judge: none。
环境
| 变量 | 默认值 | 用途 |
|---|---|---|
HOOKSHOT_STATE_DIR | <cwd>/.state | 指针、影子日志、评审数据包。将其加入 Git 忽略列表。 |
HOOKSHOT_GATE_MODE | shadow | shadow 表示记录日志;enforce 表示拒绝。阅读日志后再切换。 |
HOOKSHOT_PREFLIGHT_DOC | 未设置 | 预检提醒 Agent 阅读的仓库相对文档 |
HOOKSHOT_NO_NETWORK | 未设置 | 跳过 URL 和 PR 检查(这些检查会记录为 unverifiable) |
安装到 Claude Code(项目级 .claude/settings.json)
{
"hooks": {
"SessionStart": [{"hooks": [{"type": "command",
"command": "HOOKSHOT_PREFLIGHT_DOC=docs/preflight.md python3 \"$CLAUDE_PROJECT_DIR/.agents/skills/hookshot/scripts/preflight.py\" --client claude-code"}]}],
"Stop": [{"hooks": [{"type": "command", "timeout": 20,
"command": "HOOKSHOT_GATE_MODE=shadow python3 \"$CLAUDE_PROJECT_DIR/.agents/skills/hookshot/scripts/gate.py\" --client claude-code"}]}]
}
}
省略 SessionStart 匹配器,使其在每种来源下都会触发。Claude Code 桌面应用和 VS Code 扩展读取同一个文件。与其他模式一样,合并配置,不要覆盖。
安装到 Codex(项目级 .codex/hooks.json)
{
"hooks": {
"SessionStart": [{"hooks": [{"type": "command",
"command": "HOOKSHOT_PREFLIGHT_DOC=docs/preflight.md python3 .agents/skills/hookshot/scripts/preflight.py --client codex"}]}],
"Stop": [{"hooks": [{"type": "command", "timeout": 20,
"command": "HOOKSHOT_GATE_MODE=shadow python3 .agents/skills/hookshot/scripts/gate.py --client codex"}]}]
}
}
Codex 会从会话工作目录运行钩子。Codex 根据哈希信任每个钩子定义:安装或更改钩子后,需要有人在每台机器上的交互式 Codex 会话中运行 /hooks 并信任该钩子。不要使用 --dangerously-bypass-hook-trust 自动执行此操作。Codex Desktop 和 IDE 扩展中存在关于钩子未触发以及 Stop 阻止导致错误的未解决问题;在影子日志证明门禁正常工作之前,应将收尾步骤视为此处的门禁。
非交互式会话
对于不会触发钩子的客户端(例如忽略用户配置并启动 codex exec 的运行器),运行器会在会话结束后调用同一个脚本:
python3 .agents/skills/hookshot/scripts/gate.py --client codex-exec \
--session-id "$RUN_ID" --last-message "$LAST_MESSAGE_FILE" --cwd "$REPO"
契约指针仍为 <state>/contracts/<RUN_ID>;如果运行器已知契约路径,请传入 --contract PATH。
验证
python3 .agents/skills/hookshot/scripts/test_gate.py
echo '{"session_id":"t","cwd":"'$PWD'","source":"startup"}' | python3 .agents/skills/hookshot/scripts/preflight.py --client claude-code
echo '{"session_id":"t","cwd":"'$PWD'","stop_hook_active":false,"last_assistant_message":"DELIVERED"}' | python3 .agents/skills/hookshot/scripts/gate.py --client codex
tail -1 .state/hookshot/gate-shadow.jsonl
覆盖范围映射参考
doc-coverage.json 格式支持以下 glob 样式:
| 模式 | 匹配项 |
|---|---|
app/controllers/**/*.rb | controllers/ 下的任意 Ruby 文件 |
src/pages/**/*.tsx | pages/ 下的任意 TSX 文件 |
lib/redirect_service.rb | 精确文件 |
app/models/user.rb | 精确文件 |
**/*_mailer.rb | 任意位置的邮件程序文件 |
对高关键性文件使用具体的 glob;对领域目录使用宽泛的 glob。