mcpField
H
hk

hk

github.com/connorads开发1861 次安装2 天前更新
代码仓库

关于 hk

这是 hk(一个运行代码检查器和格式化工具的 Git 钩子管理器)的配置与设置技能。它指导检查仓库、选择钩子、编写 Pkl 配置、接入和验证,并保留项目现有约定。

主要功能

  1. 01检查仓库版本与钩子约定
  2. 02按语言和项目文件选择检查项
  3. 03使用 Pkl 配置钩子
  4. 04接入受版本控制的 Git 钩子包装器
  5. 05验证钩子计划与执行结果

适用场景

  1. 01为新仓库添加钩子
  2. 02维护现有 hk 配置
  3. 03为项目语言和工具选择检查项

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/connorads/dotfiles --skill hk

安装后在 Claude Code 中输入/hk确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/
pnpm-build-scripts-check.mjs
soft-protected-branch-pre-push.sh
evals/
prompts.md
references/
builtins-by-language.md
complete-examples.md
output-noise.md
testing-steps.md
versions-and-migration.md
tests/
pnpm-build-scripts-check.bats
soft-protected-branch-pre-push.bats

详细介绍

hk - Git 钩子管理器

hk 将代码检查器和格式化工具作为 Git 钩子运行。启用暂存时,它会协调文件访问并隔离暂存的更改。配置采用 Pkl。命令仍需准确选择文件并声明其影响。

心智模型

在编排步骤或接入钩子前,先检测仓库的版本和钩子契约。除非用户要求更改,否则维护任务应保留该契约。

设置工作流

1. 检测
hk --version                    # compare with the repo pin and schema URLs
ls package.json go.mod Cargo.toml pyproject.toml flake.nix Makefile
cat mise.toml package.json      # existing tools, package manager, scripts

新建设置时使用 hk v2。对于现有设置,请检查工具版本固定和锁文件、hk.pkl 导入、本地覆盖项以及 git config --show-origin core.hooksPath。编辑前先解决版本不匹配;不要默默升级仓库。维护 v1 或进行任何主版本升级时,请阅读 references/versions-and-migration.md。保留钩子成员、暂存预期和机密扫描范围。

识别:

  • 语言和框架
  • 包管理器(JS 使用 pnpm/bun/npm/yarn,或 cargo、go、pip 等)
  • 已配置的格式化工具(prettier、biome、ruff、gofmt 等)
  • 已配置的代码检查器(eslint、golangci-lint、ruff、clippy 等)
  • 测试运行器(vitest、jest、go test、cargo test、pytest 等)
  • 是否为团队/共享仓库,以及分支保护应采用严格的服务端保护还是建议性的本地钩子保护
2. 选择步骤(分层)

第 1 层 - 通用(始终添加):

步骤内置项
trailing-whitespaceBuiltins.trailing_whitespace
newlinesBuiltins.newlines
check-merge-conflictBuiltins.check_merge_conflict

第 2 层 - 常用工具(适用时添加):

步骤内置项适用场景
typosBuiltins.typos始终添加(快速拼写检查)
gitleaks自定义始终添加(机密检测)
rumdlBuiltins.rumdl存在 *.md 文件时

第 3 层 - 特定语言(参见 references/builtins-by-language.md):

信号文件要添加的步骤
package.json + biome.json/biome.jsoncbiome(或 ultracite)、eslint
package.json(没有 biome)prettier、eslint
tsconfig.json类型检查(tsc/tsgo/astro check/svelte-check)
go.modgo_fmt、go_vet、golangci_lint、gomod_tidy
Cargo.tomlcargo_fmt、cargo_clippy
pyproject.toml/requirements.txtruff(格式化+代码检查)、mypy
flake.nix/*.nixnix_fmt(nixfmt)、deadnix
*.sh/*.zshshfmt、shellcheck

第 4 层 - 项目特定(根据配置文件检测):

信号步骤
存在 commitlint.config.*使用 commitlint 的 commit-msg 钩子
存在 .dependency-cruiser.* 或 check:deps全图架构检查
存在 .yamllint*yamllint
团队/共享仓库no-commit-to-branch(pre-commit)、分支防护(pre-push)。对于允许所有者选择退出的建议性私有仓库保护,请使用下方的软保护 pre-push 资源。
存在 pnpm-lock.yamlpnpm 构建脚本决策检查 - 复制 assets/pnpm-build-scripts-check.mjs(见下文)
检测到测试运行器测试步骤 - vitest/jest/go test/cargo test/pytest
3. 接入钩子

需要创建或更新 3 个文件,另有可选项:

  1. mise.toml - 添加 hk 和工具二进制文件;v2 内置其 Pkl 评估器
  2. hk.pkl - 配置
  3. .hk-hooks/pre-commit - 纳入版本控制的钩子包装脚本(运行 hk run pre-commit -q;成功时,-q 会让每个步骤都保持安静 - 参见 references/output-noise.md)
  4. .hk-hooks/pre-push - 可选,用于推送时检查或分支保护。对于仅供参考的私有仓库分支保护,可复制 assets/soft-protected-branch-pre-push.sh。

然后:

chmod +x .hk-hooks/*
git config --local core.hooksPath .hk-hooks

将以下内容合并到现有的 package.json prepare 脚本中(JS 项目):

"prepare": "[ -n \"$CI\" ] && exit 0 || git config --local core.hooksPath .hk-hooks"

直接配置 core.hooksPath - 不要在此处使用 hk install。在新版 hk/Git 上,hk install 会成功并接入 hk 自己生成的钩子,从而与本技能设置的纳入版本控制的 .hk-hooks/ 包装脚本悄然产生偏差(该包装脚本添加了 HK=0 绕过机制、mise 查找和 -q)。只保留一种接入方式,即纳入版本控制的这一种。准备阶段不必安装 hk - 包装脚本会在提交时查找 hk,如果找不到则会明确报错。

对于非 JS 项目,可手动设置 core.hooksPath,或通过 Makefile 的 setup 目标设置。 保留原生的包脚本作为任务运行器。不要在 JS 项目中使用 hk init --mise 来生成替代的 mise 任务。现有安装机制需要有计划地迁移,而不是在其上再叠加第二个启动器。

4. 验证
hk validate                         # check schema and configuration
hk run pre-commit --plan             # inspect steps and selected files
hk check --all                       # execute the check hook, if defined

首选模式

新建 v2 配置时使用共享步骤

当 check、fix 和 pre-commit 使用相同检查时,使用顶层步骤。 以下 URL 以 2.0.1 为例;两处 URL 都应与选定的已安装版本一致。

amends "package://github.com/jdx/hk/releases/download/v2.0.1/[email protected]#/Config.pkl"
import "package://github.com/jdx/hk/releases/download/v2.0.1/[email protected]#/Builtins.pkl"

exclude = List("node_modules", "dist", ".next", ".git")
display_skip_reasons = List()
terminal_progress = false

steps {
    ["trailing-whitespace"] = Builtins.trailing_whitespace
    ["newlines"] = Builtins.newlines
    ["check-merge-conflict"] = Builtins.check_merge_conflict
}

这会创建 check、fix 和 pre-commit 钩子。Pre-commit 会修复、暂存并暂存收纳;check 和 fix 不会改动索引。不会创建 pre-push 钩子。 将分支保护、提交消息检查及其他特定事件的步骤保留在各自的显式钩子中。钩子覆盖会完全替换同名共享步骤;其他共享步骤仍会生效。

仅显式配置钩子的配置仍然有效。维护时保留其布局;在显式的 pre-commit 钩子中声明 fix = true 和 stash = "git"。只有在确实需要暂存时,才在其他钩子上使用 stage = true。 带有不同钩子的配置示例见 references/complete-examples.md。

排除二进制文件

对于尾随空格、换行符和拼写错误检查,务必排除二进制文件和字体文件:

local binary_excludes = List(
    "*.png", "*.jpg", "*.jpeg", "*.gif", "*.webp", "*.ico",
    "*.woff", "*.woff2", "*.ttf", "*.eot", "*.pdf", "*.zip"
)

["trailing-whitespace"] = (Builtins.trailing_whitespace) {
    exclude = binary_excludes
}
让步骤保持安静 - 在钩子包装脚本中使用一个标志

使用 hk run <hook> -q 并保留常规输出摘要。不要将其与 output_summary = "hide" 搭配使用,也不要使用 --silent:两者都会隐藏失败诊断信息。 有关支持的版本和输出验证,请参见 references/output-noise.md。

全图检查

有些工具会检查整个仓库图谱,不应接收 {{files}}: dependency-cruiser、knip、供应链扫描器、链接检查器、完整类型检查 以及覆盖率门禁。若检查必须始终查看完整图谱,请将它们配置为普通步骤且不设置 glob。(应运行哪种供应链扫描,以及阻断与报告的严重性如何划分,由供应链加固技能决定 - 本技能负责接入。)

不设置 glob 不只是为了适配工具的接受范围。如果暂存的变更只有删除操作,带 glob 的步骤根本不会运行:hk 会将 glob 解析为零个文件,步骤就不会出现在执行计划中,而不设置 glob 的步骤仍会运行(显示“0 个文件”)。因此,任何可能因删除文件而被破坏的不变量 - 例如链接检查器、无效引用检查或清单与文件树一致性门禁 - 都必须不设置 glob,否则恰恰会在其需要发挥作用的情况下不执行。

对于 dependency-cruiser:

local depcruise_step = new Step {
    check = "pnpm --silent check:deps"
}

使用包脚本,让较长的命令和配置路径保存在 JS 项目中:

"check:deps": "depcruise src --config .dependency-cruiser.cjs --output-type err-long --no-progress --no-cache"

最好将全图检查放在完整的 quality、check、CI 或 pre-push 钩子中。只有在测量步骤耗时并确认新增延迟对于日常提交可以接受后,才将其提升到暂存的 pre-commit 中。

.hk-hooks/pre-commit 包装脚本

这是 Git 实际执行的文件。它受 Git 跟踪(与 .git/hooks/ 不同)。 使用 exec,让退出状态和诊断信息传递给 Git。包装脚本提供 HK=0 绕过选项,并在 hk 不在 PATH 中时通过 mise 查找 hk。其输出约定见 references/output-noise.md。

#!/bin/sh
# hk pre-commit hook - tracked wrapper. Streams hk output directly.

# HK=0 bypasses all hooks (mirrors `HK=0 git commit`).
if [ "${HK:-1}" = "0" ]; then
  exit 0
fi

# Find hk: on PATH, else via mise (covers shells without mise activated).
HK_BIN=""
if command -v hk >/dev/null 2>&1; then
  HK_BIN="$(command -v hk)"
elif command -v mise >/dev/null 2>&1; then
  HK_BIN="$(mise which hk 2>/dev/null || true)"
fi

if [ -z "$HK_BIN" ]; then
  echo "hk not found. Install tools with: mise install" >&2
  exit 1
fi

exec "$HK_BIN" run pre-commit -q "$@"

对于只委托给 hk 的钩子,请使用更简单的包装脚本:

#!/bin/sh
exec hk run commit-msg "$@"
#!/bin/sh
exec hk run pre-push "$@"
软保护分支 pre-push

请谨慎使用:适用于小型/私有/共享仓库,在这些仓库中,服务端分支保护不可用或仅作为建议,但希望引导协作者不要直接推送到 main/master。如果可以使用服务端分支规则,请优先采用。这不是安全边界:钩子按克隆分别设置,需要 core.hooksPath,并且可以通过 --no-verify 绕过。

将 assets/soft-protected-branch-pre-push.sh 复制到 .hk-hooks/pre-push,并设置为可执行:

cp /path/to/skill/assets/soft-protected-branch-pre-push.sh .hk-hooks/pre-push
chmod +x .hk-hooks/pre-push
git config --local core.hooksPath .hk-hooks

模式:

  • 解析 Git 的 pre-push 标准输入,并根据 remote_ref 阻止推送,而不是检查当前分支。 检查当前分支会漏掉 git push origin feature:main 这类推送。
  • 默认阻止直接推送到 refs/heads/main 和 refs/heads/master。
  • 允许仓库所有者通过仓库本地配置选择退出: git config --local hooks.allowMainPush true。
  • 明确保留一次性自动化的例外方式:HK_ALLOW_MAIN_PUSH=1 git push。
  • 在仓库文档/Agent 说明中说明其建议性质及选择退出的方法。
pnpm 构建脚本决策检查

包含生命周期脚本(preinstall/install/postinstall,或 binding.gyp)的依赖,需要在 pnpm-workspace.yaml 的 allowBuilds 中记录决策。如果没有记录,pnpm 11 会以失败方式阻止安装 (ERR_PNPM_IGNORED_BUILDS)——但仅限没有任何设置掩盖其检查的情况。在全局设置了 ignoreScripts 的机器上,安装会成功,全新安装也会成功,而失败则会出现在 CI 或平台构建中。

将 assets/pnpm-build-scripts-check.mjs 复制到 .hk-hooks/,并针对可能改变依赖树的文件 glob 此步骤:

["pnpm-build-scripts"] {
    glob = List("package.json", "pnpm-lock.yaml", "pnpm-workspace.yaml")
    check = "node .hk-hooks/pnpm-build-scripts-check.mjs"
}

模式:

  • **读取配置,不运行任何内容。**检查器会解析已安装的清单和 pnpm-workspace.yaml。pnpm 没有只检测而不执行的模式,而其自身的报告(pnpm ignored-builds、.modules.yaml)是在掩盖检查的设置下生成的,因此报告的是掩盖设置,而非缺失的决策。唯一能在本地复现 CI 的命令—— pnpm install --ignore-scripts=false——会重新启用该设置所禁止的脚本,因此不能用作检查。
  • 绝不因无法评估而阻断:如果没有 node_modules,或没有 pnpm 项目,则发出警告并以 0 退出(与在全新克隆上执行类型检查步骤的处理方式一致)。
  • 提供 --json 以供机器读取;人类可读的输出会限制列表长度,并报告实际总数。
  • 软件包应设置为 true 还是 false,属于用户的安全决策——由供应链加固技能负责作出决定。此步骤只要求决策已记录。

已知限制:它读取的是已安装的依赖树,因此只能看到为当前平台解析出的可选依赖。在 linux-x64 软件包中才包含的 postinstall 脚本,对任何本地检查都不可见;只有在目标平台上运行的 CI 作业才能弥补这一缺口。

Pkl 语法参考

使用上文所示的版本化 amends 和 import 配对。内置项是否可用,取决于该架构包,而不仅仅是 PATH 中的可执行文件。

内置步骤(按原样使用)
["trailing-whitespace"] = Builtins.trailing_whitespace
内置步骤(带覆盖项)
["trailing-whitespace"] = (Builtins.trailing_whitespace) {
    exclude = List("*.png", "*.jpg")
    batch = true
}
在 v2 中覆盖内置命令

仅修改命令时,请保留内置项的 CommandSpec 效果:

["hk-test"] = (Builtins.hk_test) {
    check {
        command = "env -u GIT_DIR -u GIT_WORK_TREE hk test --quiet"
    }
}

简单的字符串赋值会替换对象,并丢失其声明的效果。修改之前,请先检查所选内置项的定义;并非每个命令都是对象。对于结构化的 argv 命令,启动器前缀应是 argv 列表,例如 prefix = List("pnpm", "exec"),而不是 shell 字符串。

自定义步骤
["typecheck"] {
    glob = List("*.ts", "*.tsx")       // optional: only run when these files staged
    check = "pnpm exec tsc --noEmit"   // silent on success - no wrapper needed
    // fix = "command to auto-fix"     // optional
}
模板变量
变量值
{{files}}经过步骤筛选器后选中的文件参数;具体选择取决于钩子和标志
{{commit_msg_file}}提交消息文件的路径(仅限 commit-msg 钩子)
{{workspace}}包含 workspace_indicator 文件的目录
{{workspace_files}}相对于工作区目录的文件
{{root}}仓库根目录。在 tests {} 块中,它仍指向实际根目录,而非沙盒目录——这样 before 才能复制检查器
{{tmp}}每项测试专用的沙盒目录。仅限 tests {};使用它会自动启用 tmpdir
分别设置钩子成员

当不同钩子需要执行不同检查时,请使用显式钩子和本地 Mapping<String, Step> 值。请参阅 references/complete-examples.md;v1 的等效内容见 references/versions-and-migration.md。

步骤排序

使用 depends = List("prettier"),让普通步骤排在 prettier 之后。组会在并行步骤集合之间创建顺序边界;语法请参阅钩子排序。在组中依赖测试之前,请先使用 hk test --list 检查测试用例是否符合预期。组发现范围的限制请参阅 references/testing-steps.md。


mise.toml 补充配置

[tools]
hk = "2"

# Add as needed based on detected steps:
typos = "latest"      # Tier 2: spell check
gitleaks = "latest"   # Tier 2: secret detection
rumdl = "latest"      # Tier 2: markdown lint (if .md files present)
yamllint = "latest"   # Tier 4: YAML lint (if .yamllint* present)

维护

添加新步骤

在 hk.pkl 的相应部分中插入。使用 hk builtins 查看可用内置步骤,或编写自定义步骤。

更新 hk 版本

如需升级,请先按照 references/versions-and-migration.md 操作,再更改工具版本固定值和两个架构 URL。使用所选二进制文件验证配置并检查钩子计划,然后运行相关检查。版本号一致本身并不能验证暂存或钩子成员配置。

临时绕过钩子
HK=0 git commit -m "wip"             # skip all hk hooks
HK_SKIP_STEPS=vitest git commit      # skip specific step
调试失败的步骤
hk check -v                          # verbose output
hk check -v --step typecheck         # single step only
hk run pre-commit -v                 # simulate hook run
本地开发者覆盖配置

使用被 git 忽略且以 amends "./hk.pkl" 开头的 hk.local.pkl。仅修改目标钩子或步骤。替换某个钩子的步骤映射不会移除继承的顶层步骤;如需移除所有共享步骤,也请替换顶层映射。请参阅本地覆盖配置参考。


注意事项

问题解决方法
pkl: command not foundV2 内置了其求值器。添加独立的 Pkl 依赖前,先检查失败的命令或 v1 后端
amends 版本不匹配使 amends/import URL 版本与 hk --version 的输出一致
内置项使用 snake_case,步骤名称使用 kebab-caseBuiltins.trailing_whitespace → ["trailing-whitespace"]
Hook 运行了,但没有匹配任何内容检查 glob 模式;使用 hk check -v 查看文件匹配情况
当 {{files}} 中没有该工具能处理的文件时,步骤失败Glob 决定步骤在哪些文件上运行,而非工具接受哪些文件:有些工具在目标集合为空时会以非零状态退出,而不是空操作。当传入的每个路径都位于其自身的 ignorePatterns 中时,oxfmt 会报错“Expected at least one target file”;没有可 lint 的文件时,oxlint 也会如此。每个步骤的 glob 应匹配该工具实际处理的文件,并将 lint 和 format 分为独立步骤——如果将它们合并为一个对 *.json 使用 glob 的步骤,那么仅修改 JSON 文件的提交就会失败
二进制文件导致拼写检查失败在 typos/trailing-whitespace/newlines 步骤中排除二进制文件
Builtins.typos 改写非英语单词,且提交通过Pre-commit 会运行该内置项的 fix(typos --write-changes),它将歌词和数据中的法语 sur 改为 sure,把更改暂存后放行了提交(2.0.1)。在 amend 中设置 fix = null 也无法阻止这一行为。对于包含非英语内容的仓库,请将该内置项替换为自定义步骤,且只包含 check = "typos --force-exclude {{files}}",并在 _typos.toml 中允许真实词语
Git worktree:hk install 失败v1.35.0 起会自动处理;如果使用较旧版本,请使用 .hk-hooks/ + core.hooksPath
修复时的暂存行为与预期不同V2 默认仅在 pre-commit 中暂存。Hook 的 stage 会启用暂存;步骤的 stage 会筛选路径。参见 references/versions-and-migration.md
成功时输出过多使用 references/output-noise.md 中的包装器和摘要设置
Hook 在 CI 中进行了不必要的运行将 [ -n "$CI" ] && exit 0 添加到 prepare 脚本
未遵循 hk.local.pkl 中的 amends第一行必须是 amends "./hk.pkl"
文档中提到的内置项无法解析内置项集合取决于 amends/import URL 中的版本,而非已安装的 hk 版本。尝试使用之前,请检查该标签下的 pkl/builtins/——例如,1.56.1 中没有 statix,但有 deadnix、lychee、check_symlinks、check_case_conflict 和 hk_test
hk check --all 似乎漏掉了文件它会选择已跟踪且符合条件的未跟踪文件。暂存会排除未跟踪文件;HK_STASH_UNTRACKED=0 会禁用对它们的发现。更改排除规则前,请先检查执行计划和生效的设置
vale 因刻意构造的格式错误 frontmatter fixture 而失败Vale 遇到无法解析的 frontmatter 时会直接报错(E201),而非跳过该文件,因此故意无效的测试 fixture 必须从该步骤中排除,方式与 lint fixture 相同
机器离线时 pinact 总是失败它会通过 GitHub API(/repos/<owner>/<repo>/commits/<ref>)解析每个 action 引用,且没有离线模式,因此 API 无法访问时会直接失败(3.10.1 中退出码为 1),这与它针对未固定的 action 报告的错误相同。请在 CI 中运行它,而非 pre-commit——这也是 hook 中的 zizmor 使用 --offline 的原因
步骤测试将 fixture 写入工作树不带 tmpdir = true 的裸相对路径会写入仓库并留下文件。请使用 {{tmp}}/...。参见 references/testing-steps.md

参考资料

  • references/versions-and-migration.md - 适用于 v1 维护、版本不匹配或请求升级到 v2 的情况
  • references/builtins-by-language.md - 按生态系统选择步骤
  • references/complete-examples.md - 适用于不同技术栈的完整 hk.pkl 配置
  • references/output-noise.md - 如何正确保持步骤静默(包装器级别的 -q、hk 的原生控制项、失败摘要注意事项)
  • references/testing-steps.md - tests {} 和 hk test:相关字段、{{tmp}} 沙盒规则、使用 before + {{root}} 测试整个仓库的检查器,以及 Builtins.hk_test 会引入哪些内容
  • assets/soft-protected-branch-pre-push.sh - 复制到 .hk-hooks/pre-push,以启用建议性的本地分支保护,并允许仅对当前克隆生效的所有者选择退出
  • tests/soft-protected-branch-pre-push.bats - 针对建议性分支保护资源的行为测试
  • assets/pnpm-build-scripts-check.mjs - 复制到 .hk-hooks/,当依赖项的构建脚本没有 allowBuilds 决策时,阻止提交
  • tests/pnpm-build-scripts-check.bats - 针对构建脚本决策检查器的行为测试
  • evals/prompts.md - 包含验收标准的设置、维护和迁移提示词
  • hk 文档 - 官方文档
  • hk builtins - 列出所有可用的内置代码检查器

评论

支持 Markdown