hookshot
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHookshot
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.
| Flag | What it adds |
|---|---|
| (no flag) | Doc coverage — PreToolUse Edit/Write hook that nudges agents to read the relevant |
| Skill drift warning — PreToolUse Edit/Write hook that warns when an agent edits a file inside |
| Markdown lint — PostToolUse Edit/Write hook that runs |
Flags compose: installs both new hooks alongside the default doc-coverage hook. Re-running hookshot merges with existing hooks idempotently.
/hookshot --drift-warn --md-lintInstall via npx:
bash
npx skills add fellowship-dev/dogfooded-skills/ops/hookshotHookshot支持组合使用——可通过一个或多个标志调用。默认模式(无标志)运行文档覆盖率生成器。
| 标志 | 新增功能 |
|---|---|
| (无标志) | 文档覆盖率 — 一种PreToolUse编辑/写入钩子,会提示Agent在编辑被覆盖的文件前,先阅读docs/目录下的相关章节。(默认行为,下文有详细说明。) |
| 技能漂移警告 — 一种PreToolUse编辑/写入钩子,当Agent编辑 |
| Markdown语法检查 — 一种PostToolUse编辑/写入钩子,会对任何被修改的 |
标志可组合使用: 会在默认的文档覆盖率钩子之外,同时安装这两个新钩子。重新运行Hookshot会以幂等方式与现有钩子合并。
/hookshot --drift-warn --md-lint通过npx安装:
bash
npx skills add fellowship-dev/dogfooded-skills/ops/hookshotWhen to Use
使用场景
- After creates the knowledge layer — hookshot wires it to the agent runtime
/setup-harness - After updating or
docs/code-structure.md— regenerate hooks to stay currentdocs/code-guidelines.md - When the occurs: agent modified a critical path without reading docs — add a hook to prevent recurrence
#1585-class bug - After adding a new FlowChad flow — generate hooks for that critical path
- 在创建知识层之后——Hookshot会将其与Agent运行时连接起来
/setup-harness - 更新或
docs/code-structure.md之后——重新生成钩子以保持同步docs/code-guidelines.md - 当出现bug时:Agent未阅读文档就修改了关键路径——添加钩子以防止此类情况再次发生
#1585-class - 添加新的FlowChad流程之后——为该关键路径生成钩子
Integration with Pylot
与Pylot的集成
- Installation: installs the hookshot skill via
boot-skills.sh. But installing the skill ≠ generating hooks. You must runnpx skills addat least once to generate the artifacts (/hookshot,doc-coverage.json,check-docs.shhooks,settings.json).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: monitors hookshot coverage freshness on PR merge and weekly cron. When it flags staleness, re-run
entropy-check./hookshot - Per-repo: Each repo gets its own hooks. Cross-repo missions use the target repo's hooks.
- 安装: 会通过
boot-skills.sh安装Hookshot技能。但安装技能≠生成钩子。你必须至少运行一次npx skills add来生成相关产物(/hookshot、doc-coverage.json、check-docs.sh钩子、settings.json)。docs/hooks.md - setup-harness: 在首次设置的最后阶段会运行Hookshot。完成setup-harness后,你无需单独运行Hookshot。
- 过期检测: 会在PR合并时和每周定时任务中监控Hookshot覆盖率的新鲜度。当检测到过期时,重新运行
entropy-check。/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
works before modifying this area" — this skill generates that message from your docs.
check_redirect"由于语法检查是自定义的,我们可以编写错误消息来将修复说明注入到Agent的上下文环境中。" —— OpenAI harness工程团队
Hookshot可自动实现这一点。Agent会收到类似“在修改该区域前,请先了解的工作原理”的提示——该技能会从你的文档中生成这类提示信息。
check_redirectWhat It Generates
生成产物
- — Given a file being edited, outputs a doc reminder to stderr if that file is covered by docs/
check-docs.sh - hooks — PreToolUse hook calling
.claude/settings.jsonon every Edit/Writecheck-docs.sh - Domain coverage map — — maps file globs to doc sections
$REPO_ROOT/.claude/doc-coverage.json - Custom lint messages — Remediation instructions with specific doc section links
- — 当给定一个待编辑的文件时,如果该文件被docs/目录下的文档覆盖,会将文档提醒输出到stderr
check-docs.sh - 钩子 — 调用
.claude/settings.json的PreToolUse钩子,适用于所有编辑/写入操作check-docs.sh - 领域覆盖映射表 — — 将文件通配符映射到文档章节
$REPO_ROOT/.claude/doc-coverage.json - 自定义语法检查消息 — 带有特定文档章节链接的修复说明
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/.claudebash
REPO_ROOT=$(git rev-parse --show-toplevel)
REPO_NAME=$(basename $(git remote get-url origin) .git)
mkdir -p $REPO_ROOT/.claude1. Parse Knowledge Layer
1. 解析知识层
Read all documentation files and extract coverage mappings:
bash
undefined读取所有文档文件并提取覆盖映射关系:
bash
undefinedRead 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.jsonjson
{
"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.jsonjson
{
"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.shbash
#!/usr/bin/env bash编写:
$REPO_ROOT/.claude/check-docs.shbash
#!/usr/bin/env bashcheck-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.shREMINDERS=$(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.sh4. Write Hooks to settings.json
4. 向settings.json写入钩子
Read the existing if it exists.
Merge in the hooks configuration — do not clobber existing hooks.
.claude/settings.jsonThe 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 already exists → add to the array, don't replace
PreToolUse - If an identical hook already exists → skip (idempotent)
check-docs.sh - 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.json5. Generate Custom Lint Messages
5. 生成自定义语法检查消息
For each guideline in , generate a lint message file:
docs/code-guidelines.mdCreate :
$REPO_ROOT/.claude/lint-messages.mdmarkdown
undefined针对中的每条准则,生成一个语法检查消息文件:
docs/code-guidelines.md创建:
$REPO_ROOT/.claude/lint-messages.mdmarkdown
undefinedCustom 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`:
```bashPattern 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`中:
```bashAdd 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 file in the target repo documenting the active hooks:
docs/hooks.mdmarkdown
undefined在目标仓库中生成文件,记录当前激活的钩子:
docs/hooks.mdmarkdown
undefinedHooks — {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 or tool call
Script: (or )
Config: →
Coverage map:
EditWritescripts/check-docs.sh.claude/check-docs.sh.claude/settings.jsonhooks.PreToolUse.claude/doc-coverage.jsonWhen 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 or tool call
Script: (or )
Config: →
Coverage map:
EditWritescripts/check-docs.sh.claude/check-docs.sh.claude/settings.jsonhooks.PreToolUse.claude/doc-coverage.jsonWhen 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:}
| Domain | Glob | Criticality | Reminder |
|---|---|---|---|
| {domain} | | {criticality} | {reminder} |
{For each entry in doc-coverage.json, list:}
| Domain | Glob | Criticality | Reminder |
|---|---|---|---|
| {domain} | | {criticality} | {reminder} |
Maintaining Hooks
Maintaining Hooks
- Quick tweaks: Edit directly — add/remove entries, adjust criticality or reminder text. Changes take effect immediately.
.claude/doc-coverage.json - Full regeneration: Run to rebuild coverage map from current
/hookshot. This merges with your existingdocs/code-structure.mdanddoc-coverage.json— it won't overwrite manual additions.docs/hooks.md - Staleness detection: monitors whether hooks are current vs docs. If it flags staleness, re-run
/entropy-check./hookshot
- Quick tweaks: Edit directly — add/remove entries, adjust criticality or reminder text. Changes take effect immediately.
.claude/doc-coverage.json - Full regeneration: Run to rebuild coverage map from current
/hookshot. This merges with your existingdocs/code-structure.mdanddoc-coverage.json— it won't overwrite manual additions.docs/hooks.md - Staleness detection: monitors whether hooks are current vs docs. If it flags staleness, re-run
/entropy-check./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*/ - are character classes, not literal — Next.js routes like
[brackets]need escaping[locale] - The generated normalizes both automatically. If you're writing manual globs, use
check-docs.shnot*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*/ - are character classes, not literal — Next.js routes like
[brackets]need escaping[locale] - The generated normalizes both automatically. If you're writing manual globs, use
check-docs.shnot*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 . Re-run to restore.
.claude/settings.json/hookshotDisable temporarily by removing the hook entry from . Re-run to restore.
.claude/settings.json/hookshotsettings.json got clobbered
settings.json got clobbered
Re-run — it merges hooks into existing settings, never overwrites other config.
/hookshot
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 — it merges hooks into existing settings, never overwrites other config.
/hookshot
重新运行时,读取现有的`docs/hooks.md`并合并:保留所有人工添加的章节,根据当前状态重新生成“Covered Domains”表格和“Active Hooks”章节。6. Verification
6. 验证
Test the generated hook:
bash
undefined测试生成的钩子:
bash
undefinedTest 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"
undefinedcat $REPO_ROOT/.claude/doc-coverage.json | jq . > /dev/null && echo "doc-coverage.json: valid JSON"
undefined7. Summary Report
7. 总结报告
undefinedundefinedHookshot 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模式:漂移警告 (--drift-warn
)
--drift-warnWarns when an agent is about to edit a file inside a dir for a skill that's tracked in . The actual edit is not blocked — this is guidance, and agents sometimes legitimately need to hotfix a synced skill before upstreaming.
.claude/skills/<name>/skills-lock.json当Agent即将编辑目录下、且在中被跟踪的技能文件时,会发出警告。实际编辑操作不会被阻止——这只是指引,Agent有时确实需要在提交到上游前对同步的技能进行热修复。
.claude/skills/<name>/skills-lock.jsonGenerate .claude/check-skill-drift.sh
.claude/check-skill-drift.sh生成.claude/check-skill-drift.sh
.claude/check-skill-drift.shbash
#!/usr/bin/env bashbash
#!/usr/bin/env bashcheck-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
undefinedbash
undefinedShould 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
)
--md-lint模式:Markdown语法检查 (--md-lint
)
--md-lintRuns on any changed file after an Edit or Write, and surfaces the warnings to the agent. Never auto-fixes — agent decides.
npx markdownlint-cli2.md在编辑或写入操作完成后,对任何被修改的文件运行,并向Agent显示警告。绝不自动修复——由Agent决定是否修复。
.mdnpx markdownlint-cli2Starter .markdownlint.json
.markdownlint.json初始.markdownlint.json
.markdownlint.jsonIf the repo has no or at root, drop a permissive starter so the linter isn't overwhelming out of the box:
.markdownlint.json.markdownlint-cli2.jsoncjson
{
"default": true,
"MD013": false,
"MD033": false,
"MD041": false
}- (line length) — off by default; docs and skill files often have long lines
MD013 - (inline HTML) — off; we use HTML details/summary in reports
MD033 - (first line must be h1) — off; many docs start with frontmatter
MD041
If a config already exists, leave it. Never overwrite.
如果仓库根目录下没有或,则添加一个宽松的初始配置,避免默认情况下语法检查过于严格:
.markdownlint.json.markdownlint-cli2.jsoncjson
{
"default": true,
"MD013": false,
"MD033": false,
"MD041": false
}- (行长度)——默认关闭;文档和技能文件通常包含长行
MD013 - (内嵌HTML)——关闭;我们在报告中使用HTML的details/summary标签
MD033 - (首行必须为h1)——关闭;许多文档以前置元数据开头
MD041
如果已有配置文件,则保留原文件,绝不覆盖。
Generate .claude/check-md-lint.sh
.claude/check-md-lint.sh生成.claude/check-md-lint.sh
.claude/check-md-lint.shbash
#!/usr/bin/env bashbash
#!/usr/bin/env bashcheck-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
undefinedbash
undefinedShould 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 format supports these glob styles:
doc-coverage.json| Pattern | Matches |
|---|---|
| Any Ruby file under controllers/ |
| Any TSX file under pages/ |
| Exact file |
| Exact file |
| Any mailer anywhere |
Use specific globs for high-criticality files. Use broad globs for domain directories.
doc-coverage.json| 模式 | 匹配内容 |
|---|---|
| controllers/目录下的所有Ruby文件 |
| pages/目录下的所有TSX文件 |
| 精确匹配该文件 |
| 精确匹配该文件 |
| 任意位置的所有mailer文件 |
对于高优先级文件使用特定的通配符,对于领域目录使用宽泛的通配符。