hookshot

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Hookshot

Hookshot

From docs to enforcement hooks. Reads what you've documented. Generates the hooks that make agents read it.
Philosophy: Hookshot only issues warnings and guidance. It never amends files, never lints-and-fixes. All hooks it generates emit messages to stderr and exit 0 — the agent decides whether to act.
从文档到执行钩子。读取你已编写的文档内容,生成能让Agent阅读这些文档的钩子。
设计理念: Hookshot仅发出警告和指引,从不修改文件,也不执行自动修复。它生成的所有钩子都会将消息输出到stderr并以0状态退出——由Agent决定是否采取行动。

Modes

模式

Hookshot is composable — invoke with one or more flags. Default mode (no flags) runs the doc-coverage generator.
FlagWhat it adds
(no flag)Doc coverage — PreToolUse Edit/Write hook that nudges agents to read the relevant
docs/
section before editing a covered file. (Default behavior, documented below.)
--drift-warn
Skill drift warning — PreToolUse Edit/Write hook that warns when an agent edits a file inside
.claude/skills/<name>/
for a skill that's tracked in
skills-lock.json
. Edits should go upstream.
--md-lint
Markdown lint — PostToolUse Edit/Write hook that runs
npx markdownlint-cli2
on any changed
*.md
file and surfaces warnings. Never auto-fixes.
Flags compose:
/hookshot --drift-warn --md-lint
installs both new hooks alongside the default doc-coverage hook. Re-running hookshot merges with existing hooks idempotently.
Install via npx:
bash
npx skills add fellowship-dev/dogfooded-skills/ops/hookshot
Hookshot支持组合使用——可通过一个或多个标志调用。默认模式(无标志)运行文档覆盖率生成器。
标志新增功能
(无标志)文档覆盖率 — 一种PreToolUse编辑/写入钩子,会提示Agent在编辑被覆盖的文件前,先阅读docs/目录下的相关章节。(默认行为,下文有详细说明。)
--drift-warn
技能漂移警告 — 一种PreToolUse编辑/写入钩子,当Agent编辑
.claude/skills/<name>/
目录下、且在
skills-lock.json
中被跟踪的技能文件时,会发出警告。编辑操作应提交到上游仓库。
--md-lint
Markdown语法检查 — 一种PostToolUse编辑/写入钩子,会对任何被修改的
*.md
文件运行
npx markdownlint-cli2
并显示警告。绝不自动修复。
标志可组合使用:
/hookshot --drift-warn --md-lint
会在默认的文档覆盖率钩子之外,同时安装这两个新钩子。重新运行Hookshot会以幂等方式与现有钩子合并。
通过npx安装:
bash
npx skills add fellowship-dev/dogfooded-skills/ops/hookshot

When to Use

使用场景

  • After
    /setup-harness
    creates the knowledge layer — hookshot wires it to the agent runtime
  • After updating
    docs/code-structure.md
    or
    docs/code-guidelines.md
    — regenerate hooks to stay current
  • When the
    #1585-class bug
    occurs: agent modified a critical path without reading docs — add a hook to prevent recurrence
  • After adding a new FlowChad flow — generate hooks for that critical path
  • /setup-harness
    创建知识层之后——Hookshot会将其与Agent运行时连接起来
  • 更新
    docs/code-structure.md
    docs/code-guidelines.md
    之后——重新生成钩子以保持同步
  • 当出现
    #1585-class
    bug时:Agent未阅读文档就修改了关键路径——添加钩子以防止此类情况再次发生
  • 添加新的FlowChad流程之后——为该关键路径生成钩子

Integration with Pylot

与Pylot的集成

  • Installation:
    boot-skills.sh
    installs the hookshot skill via
    npx skills add
    . But installing the skill ≠ generating hooks. You must run
    /hookshot
    at least once to generate the artifacts (
    doc-coverage.json
    ,
    check-docs.sh
    ,
    settings.json
    hooks,
    docs/hooks.md
    ).
  • setup-harness: Runs hookshot as its final phase on first setup. You don't need to run hookshot separately after setup-harness.
  • Staleness:
    entropy-check
    monitors hookshot coverage freshness on PR merge and weekly cron. When it flags staleness, re-run
    /hookshot
    .
  • Per-repo: Each repo gets its own hooks. Cross-repo missions use the target repo's hooks.
  • 安装:
    boot-skills.sh
    会通过
    npx skills add
    安装Hookshot技能。但安装技能≠生成钩子。你必须至少运行一次
    /hookshot
    来生成相关产物(
    doc-coverage.json
    check-docs.sh
    settings.json
    钩子、
    docs/hooks.md
    )。
  • setup-harness: 在首次设置的最后阶段会运行Hookshot。完成setup-harness后,你无需单独运行Hookshot。
  • 过期检测:
    entropy-check
    会在PR合并时和每周定时任务中监控Hookshot覆盖率的新鲜度。当检测到过期时,重新运行
    /hookshot
  • 按仓库划分: 每个仓库都有自己的钩子。跨仓库任务会使用目标仓库的钩子。

Key Insight

核心思路

"Because the lints are custom, we write the error messages to inject remediation instructions into agent context." — OpenAI harness engineering
Hookshot makes this automatic. The agent would have been told "read how
check_redirect
works before modifying this area" — this skill generates that message from your docs.
"由于语法检查是自定义的,我们可以编写错误消息来将修复说明注入到Agent的上下文环境中。" —— OpenAI harness工程团队
Hookshot可自动实现这一点。Agent会收到类似“在修改该区域前,请先了解
check_redirect
的工作原理”的提示——该技能会从你的文档中生成这类提示信息。

What It Generates

生成产物

  1. check-docs.sh
    — Given a file being edited, outputs a doc reminder to stderr if that file is covered by docs/
  2. .claude/settings.json
    hooks
    — PreToolUse hook calling
    check-docs.sh
    on every Edit/Write
  3. Domain coverage map
    $REPO_ROOT/.claude/doc-coverage.json
    — maps file globs to doc sections
  4. Custom lint messages — Remediation instructions with specific doc section links

  1. check-docs.sh
    — 当给定一个待编辑的文件时,如果该文件被docs/目录下的文档覆盖,会将文档提醒输出到stderr
  2. .claude/settings.json
    钩子
    — 调用
    check-docs.sh
    的PreToolUse钩子,适用于所有编辑/写入操作
  3. 领域覆盖映射表
    $REPO_ROOT/.claude/doc-coverage.json
    — 将文件通配符映射到文档章节
  4. 自定义语法检查消息 — 带有特定文档章节链接的修复说明

Instructions

操作步骤

0. Identify the Repo

0. 确定仓库

bash
REPO_ROOT=$(git rev-parse --show-toplevel)
REPO_NAME=$(basename $(git remote get-url origin) .git)
mkdir -p $REPO_ROOT/.claude
bash
REPO_ROOT=$(git rev-parse --show-toplevel)
REPO_NAME=$(basename $(git remote get-url origin) .git)
mkdir -p $REPO_ROOT/.claude

1. Parse Knowledge Layer

1. 解析知识层

Read all documentation files and extract coverage mappings:
bash
undefined
读取所有文档文件并提取覆盖映射关系:
bash
undefined

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

For each domain section in `docs/code-structure.md`, extract:
- **Domain name** (section header)
- **Directory path** (the "Directory:" line)
- **Key files** (from the Entry Points table)
- **Critical patterns** ("Don't Repeat" section — these are the highest priority)

For each FlowChad flow in `.flowchad/flows/`:
- **Flow name**
- **Domain**
- **Entry point file** (from the flow definition)
- **Files touched** (all `file:` entries in the flow)
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:”行)
- **关键文件**(来自入口点表格)
- **关键规则**(“Don't Repeat”章节——这些是最高优先级)

对于`.flowchad/flows/`中的每个FlowChad流程:
- **流程名称**
- **领域**
- **入口点文件**(来自流程定义)
- **涉及文件**(流程中所有`file:`条目)

2. Build Coverage Map

2. 构建覆盖映射表

Create
$REPO_ROOT/.claude/doc-coverage.json
:
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"
    }
  ]
}
Build one entry per domain directory mapping. For critical paths (found in FlowChad flows), set
criticality: "high"
.
Reminder text rules:
  • Lead with the specific doc section to read
  • Include the most important "Don't Repeat" rule for that domain
  • Keep under 200 characters — this appears in agent context, not a wall of text
  • Be actionable: "Read X" not "Consider reading X"
创建
$REPO_ROOT/.claude/doc-coverage.json
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. Generate check-docs.sh

3. 生成check-docs.sh

Write
$REPO_ROOT/.claude/check-docs.sh
:
bash
#!/usr/bin/env bash
编写
$REPO_ROOT/.claude/check-docs.sh
bash
#!/usr/bin/env bash

check-docs.sh — Generated by hookshot on {DATE}

check-docs.sh — Generated by hookshot on {DATE}

Usage: check-docs.sh <file_path_being_edited>

Usage: check-docs.sh <file_path_being_edited>

Outputs doc reminders to stderr if the file is covered by docs/

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
set -euo pipefail
FILE_PATH="${1:-}" if [ -z "$FILE_PATH" ]; then exit 0 fi

Normalize path relative to repo root

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
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

Check file against each glob in the coverage map

Uses jq to parse coverage map and bash glob matching

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

Make it executable:
```bash
chmod +x $REPO_ROOT/.claude/check-docs.sh
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

设置可执行权限:
```bash
chmod +x $REPO_ROOT/.claude/check-docs.sh

4. Write Hooks to settings.json

4. 向settings.json写入钩子

Read the existing
.claude/settings.json
if it exists. Merge in the hooks configuration — do not clobber existing hooks.
The hook to add:
json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "bash {REPO_ROOT}/scripts/check-docs.sh \"$(jq -r '.tool_input.file_path // empty')\""
          }
        ]
      }
    ]
  }
}
Merge strategy:
  • If
    PreToolUse
    already exists → add to the array, don't replace
  • If an identical
    check-docs.sh
    hook already exists → skip (idempotent)
  • Preserve all existing hook entries
Write the merged result back to
.claude/settings.json
.
读取现有的
.claude/settings.json
(如果存在)。 合并钩子配置——不要覆盖现有钩子。
需要添加的钩子:
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. Generate Custom Lint Messages

5. 生成自定义语法检查消息

For each guideline in
docs/code-guidelines.md
, generate a lint message file:
Create
$REPO_ROOT/.claude/lint-messages.md
:
markdown
undefined
针对
docs/code-guidelines.md
中的每条准则,生成一个语法检查消息文件:
创建
$REPO_ROOT/.claude/lint-messages.md
markdown
undefined

Custom Lint Messages — {REPO_NAME}

Custom Lint Messages — {REPO_NAME}

Generated by hookshot on {DATE}. Used by PreToolUse hooks to inject remediation context.
Generated by hookshot on {DATE}. Used by PreToolUse hooks to inject remediation context.

{Domain}: {Rule Name}

{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}

For critical rules (e.g., "never roll your own auth", "use check_redirect not inline conditionals"), generate explicit check commands to add to `check-docs.sh`:

```bash
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}

对于关键规则(例如“never roll your own auth”、“use check_redirect not inline conditionals”),生成明确的检查命令并添加到`check-docs.sh`中:

```bash

Add to check-docs.sh after the glob check:

Add to check-docs.sh after the glob check:

Rule-based checks (pattern detection in file content)

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

Add these rule-based checks to `check-docs.sh` in a clearly marked section.
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. Generate docs/hooks.md

5b. 生成docs/hooks.md

Generate a
docs/hooks.md
file in the target repo documenting the active hooks:
markdown
undefined
在目标仓库中生成
docs/hooks.md
文件,记录当前激活的钩子:
markdown
undefined

Hooks — {REPO_NAME}

Hooks — {REPO_NAME}

Auto-generated by hookshot. Safe to add notes — hookshot merges on update, it won't overwrite your additions.
Auto-generated by hookshot. Safe to add notes — hookshot merges on update, it won't overwrite your additions.

Active Hooks

Active Hooks

PreToolUse: Doc Reminders on Edit/Write

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.
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

Covered Domains

{For each entry in doc-coverage.json, list:}
DomainGlobCriticalityReminder
{domain}
{glob}
{criticality}{reminder}
{For each entry in doc-coverage.json, list:}
DomainGlobCriticalityReminder
{domain}
{glob}
{criticality}{reminder}

Maintaining Hooks

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
    .
  • 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

Troubleshooting

Glob doesn't match expected files

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"
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

Hook breaks the agent or slows edits

Disable temporarily by removing the hook entry from
.claude/settings.json
. Re-run
/hookshot
to restore.
Disable temporarily by removing the hook entry from
.claude/settings.json
. Re-run
/hookshot
to restore.

settings.json got clobbered

settings.json got clobbered

Re-run
/hookshot
— it merges hooks into existing settings, never overwrites other config.

On re-runs, read the existing `docs/hooks.md` and merge: preserve any human-added sections, regenerate the "Covered Domains" table and "Active Hooks" section from current state.
Re-run
/hookshot
— it merges hooks into existing settings, never overwrites other config.

重新运行时,读取现有的`docs/hooks.md`并合并:保留所有人工添加的章节,根据当前状态重新生成“Covered Domains”表格和“Active Hooks”章节。

6. Verification

6. 验证

Test the generated hook:
bash
undefined
测试生成的钩子:
bash
undefined

Test with a file that should trigger a reminder

Test with a file that should trigger a reminder

bash $REPO_ROOT/.claude/check-docs.sh "$REPO_ROOT/app/controllers/sessions_controller.rb"
bash $REPO_ROOT/.claude/check-docs.sh "$REPO_ROOT/app/controllers/sessions_controller.rb"

Test with a file that should NOT trigger

Test with a file that should NOT trigger

bash $REPO_ROOT/.claude/check-docs.sh "$REPO_ROOT/README.md"
bash $REPO_ROOT/.claude/check-docs.sh "$REPO_ROOT/README.md"

Verify settings.json is valid JSON

Verify settings.json is valid JSON

cat $REPO_ROOT/.claude/settings.json | jq . > /dev/null && echo "settings.json: valid JSON"
cat $REPO_ROOT/.claude/settings.json | jq . > /dev/null && echo "settings.json: valid JSON"

Verify doc-coverage.json is 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"
undefined
cat $REPO_ROOT/.claude/doc-coverage.json | jq . > /dev/null && echo "doc-coverage.json: valid JSON"
undefined

7. Summary Report

7. 总结报告

undefined
undefined

Hookshot Complete: {REPO_NAME}

Hookshot Complete: {REPO_NAME}

Coverage Map

Coverage Map

  • {N} domain entries in .claude/doc-coverage.json
  • {N} high-criticality entries (will produce warnings)
  • {N} medium-criticality entries (will produce reminders)
  • {N} domain entries in .claude/doc-coverage.json
  • {N} high-criticality entries (will produce warnings)
  • {N} medium-criticality entries (will produce reminders)

Hooks Generated

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
  • .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

Coverage Gaps

{List any domains in ARCHITECTURE.md that have no glob coverage — need manual mapping}
{List any domains in ARCHITECTURE.md that have no glob coverage — need manual mapping}

Manual Next Steps

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

---
  • 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

---

Mode: Drift Warning (
--drift-warn
)

模式:漂移警告 (
--drift-warn
)

Warns when an agent is about to edit a file inside a
.claude/skills/<name>/
dir for a skill that's tracked in
skills-lock.json
. The actual edit is not blocked — this is guidance, and agents sometimes legitimately need to hotfix a synced skill before upstreaming.
当Agent即将编辑
.claude/skills/<name>/
目录下、且在
skills-lock.json
中被跟踪的技能文件时,会发出警告。实际编辑操作不会被阻止——这只是指引,Agent有时确实需要在提交到上游前对同步的技能进行热修复。

Generate
.claude/check-skill-drift.sh

生成
.claude/check-skill-drift.sh

bash
#!/usr/bin/env bash
bash
#!/usr/bin/env bash

check-skill-drift.sh — Generated by hookshot (--drift-warn) on {DATE}

check-skill-drift.sh — Generated by hookshot (--drift-warn) on {DATE}

Usage: check-skill-drift.sh <file_path>

Usage: check-skill-drift.sh <file_path>

Warns to stderr if the file belongs to a skill tracked in skills-lock.json.

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
set -uo pipefail
FILE_PATH="${1:-}" [ -z "$FILE_PATH" ] && exit 0

Walk up from the file to find the nearest skills-lock.json

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
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>/

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
SKILL_NAME="$(echo "$FILE_PATH" | sed -nE 's|./.(claude|agents)/skills/([^/]+)/.|\2|p')" [ -z "$SKILL_NAME" ] && exit 0

Look up in lockfile

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

Make it executable and wire into `.claude/settings.json` under `PreToolUse` with matcher `Edit|Write`. Merge-don't-clobber, same strategy as the default doc-coverage hook.
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

设置可执行权限,并将其添加到`.claude/settings.json`的`PreToolUse`下,匹配器为`Edit|Write`。采用与默认文档覆盖率钩子相同的“合并而非覆盖”策略。

Verification

验证

bash
undefined
bash
undefined

Should warn — cto-review is a tracked skill

Should warn — cto-review is a tracked skill

bash .claude/check-skill-drift.sh "$PWD/.claude/skills/cto-review/SKILL.md"
bash .claude/check-skill-drift.sh "$PWD/.claude/skills/cto-review/SKILL.md"

Should be silent — file is outside any skills dir

Should be silent — file is outside any skills dir

bash .claude/check-skill-drift.sh "$PWD/README.md"
bash .claude/check-skill-drift.sh "$PWD/README.md"

Should be silent — skill isn't in lockfile (e.g. a local-only skill)

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"

---
bash .claude/check-skill-drift.sh "$PWD/.claude/skills/local-thing/SKILL.md"

---

Mode: Markdown Lint (
--md-lint
)

模式:Markdown语法检查 (
--md-lint
)

Runs
npx markdownlint-cli2
on any changed
.md
file after an Edit or Write, and surfaces the warnings to the agent. Never auto-fixes — agent decides.
在编辑或写入操作完成后,对任何被修改的
.md
文件运行
npx markdownlint-cli2
,并向Agent显示警告。绝不自动修复——由Agent决定是否修复。

Starter
.markdownlint.json

初始
.markdownlint.json

If the repo has no
.markdownlint.json
or
.markdownlint-cli2.jsonc
at root, drop a permissive starter so the linter isn't overwhelming out of the box:
json
{
  "default": true,
  "MD013": false,
  "MD033": false,
  "MD041": false
}
  • MD013
    (line length) — off by default; docs and skill files often have long lines
  • MD033
    (inline HTML) — off; we use HTML details/summary in reports
  • MD041
    (first line must be h1) — off; many docs start with frontmatter
If a config already exists, leave it. Never overwrite.
如果仓库根目录下没有
.markdownlint.json
.markdownlint-cli2.jsonc
,则添加一个宽松的初始配置,避免默认情况下语法检查过于严格:
json
{
  "default": true,
  "MD013": false,
  "MD033": false,
  "MD041": false
}
  • MD013
    (行长度)——默认关闭;文档和技能文件通常包含长行
  • MD033
    (内嵌HTML)——关闭;我们在报告中使用HTML的details/summary标签
  • MD041
    (首行必须为h1)——关闭;许多文档以前置元数据开头
如果已有配置文件,则保留原文件,绝不覆盖。

Generate
.claude/check-md-lint.sh

生成
.claude/check-md-lint.sh

bash
#!/usr/bin/env bash
bash
#!/usr/bin/env bash

check-md-lint.sh — Generated by hookshot (--md-lint) on {DATE}

check-md-lint.sh — Generated by hookshot (--md-lint) on {DATE}

Usage: check-md-lint.sh <file_path>

Usage: check-md-lint.sh <file_path>

Runs markdownlint-cli2 on the file if it's *.md. Warns only — never fixes.

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
set -uo pipefail
FILE_PATH="${1:-}" [ -z "$FILE_PATH" ] && exit 0

Only lint markdown files

Only lint markdown files

case "$FILE_PATH" in .md|.markdown) ;; *) exit 0 ;; esac
[ -f "$FILE_PATH" ] || exit 0
case "$FILE_PATH" in .md|.markdown) ;; *) exit 0 ;; esac
[ -f "$FILE_PATH" ] || exit 0

Run markdownlint-cli2 — fast start via npx

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

Wire into `.claude/settings.json` under **`PostToolUse`** (not PreToolUse — the file must exist before it can be linted) with matcher `Edit|Write`.
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

将其添加到`.claude/settings.json`的**`PostToolUse`**下(而非PreToolUse——文件必须先存在才能进行语法检查),匹配器为`Edit|Write`。

Verification

验证

bash
undefined
bash
undefined

Should print MD### warnings if the file has any lint issues

Should print MD### warnings if the file has any lint issues

bash .claude/check-md-lint.sh README.md
bash .claude/check-md-lint.sh README.md

Should be silent — not a markdown file

Should be silent — not a markdown file

bash .claude/check-md-lint.sh package.json

---
bash .claude/check-md-lint.sh package.json

---

Coverage Map Reference

覆盖映射表参考

The
doc-coverage.json
format supports these glob styles:
PatternMatches
app/controllers/**/*.rb
Any Ruby file under controllers/
src/pages/**/*.tsx
Any TSX file under pages/
lib/redirect_service.rb
Exact file
app/models/user.rb
Exact file
**/*_mailer.rb
Any mailer anywhere
Use specific globs for high-criticality files. Use broad globs for domain directories.
doc-coverage.json
格式支持以下通配符样式:
模式匹配内容
app/controllers/**/*.rb
controllers/目录下的所有Ruby文件
src/pages/**/*.tsx
pages/目录下的所有TSX文件
lib/redirect_service.rb
精确匹配该文件
app/models/user.rb
精确匹配该文件
**/*_mailer.rb
任意位置的所有mailer文件
对于高优先级文件使用特定的通配符,对于领域目录使用宽泛的通配符。