Two configuration expansions land in this release: shell-free argv commands with per-file argument expansion, and match_any selectors that combine globs and types with OR semantics. Alongside them, correctness fixes for --quiet/--silent, config-based hook argument forwarding, transient index locks, and Linux argument-size limits.
Added
-
Structured
argvcommands (@jdx) #1067. A step'scheck/fix/check_list_files/check_diffcan now be aCommandwith an explicitargvlist that runs a binary viaPATHwithout a shell. Standalone{{files}}and{{workspace_files}}entries expand to one argument per file (raw paths), so names with spaces or shell metacharacters are passed literally. Auto-batching,hk test, progress output, and fix suggestions all go through sharedCommandrendering, and many builtins (prettier, ruff, biome, ...) have moved to structured commands. Existing string and platformScriptcommands are unchanged; structured commands can't be combined withshellorprefix.check = new Command { argv = List("wc", "-c", "{{files}}") }
-
match_anydisjunctive file selectors (@risu729) #1055. Clauses compose with OR semantics;globandtypeswithin a clause compose with AND.match_anycannot be mixed with the top-levelglobortypes, and empty selectors are rejected at validation time.{{globs}}and progress text reflect the combined patterns. Theshellcheckandshfmtbuiltins have adopted it so they also pick up extensionless shell scripts detected by shebang, without giving up their extension globs.["shellcheck"] { match_any = List( new { glob = List("**/*.sh", "**/*.bash") }, new { types = List("sh", "bash") } ) check = "shellcheck {{files}}" }
-
sherifbuiltin (@smasato) #1062. Adds sherif, the opinionated zero-config linter for TypeScript/JavaScript monorepos.check = "sherif"/fix = "sherif --fix --no-install"; sherif always scans from the repo root, so the commands take no file arguments. Globs cover**/package.jsonand**/pnpm-workspace.yaml; auto-suggested when"sherif"appears inpackage.json.["sherif"] = Builtins.sherif
Fixed
-
--quietand--silentreally suppress output now (@smasato) #1058. Previously these flags only lowered the log level and switched progress to text mode, which streams every update on a new line rather than hiding anything. They now setProgressOutput::Quietso progress lines and successful-step summaries are gone;--quietstill prints failed-step summaries (essential diagnostic),--silentprints nothing but exit code.--statsis suppressed under both. Informationalprintln!s ininit/install/migratewere routed throughinfo!so the flags apply there too. CI/non-interactive text-mode behavior is unchanged. -
Config-based hooks no longer duplicate arguments (@jdx) #1065. Git 2.54+ already appends hook arguments to
hook.<name>.command, and hk was also expanding"$@", so argument-bearing hooks received every argument twice. Forpre-pushthis meant the duplicated remote/URL were parsed as an explicit file list, which overrode push-range discovery and silently skipped file-filtered steps.hook_run_argsno longer appends"$@"; legacy.git/hooks/shims still use it viagit_hook_content. Fixes discussion #1063. -
Stash waits out transient index locks (@jdx) #1060. Shell-based stash paths now resolve the worktree lock via
git rev-parse --git-path index.lockand sleep with bounded backoff (up to ~775ms) before runninggit stash push, so a briefly held lock from another Git process no longer aborts the hook. A persistent lock still surfaces as a normal Git error after the wait window; libgit2stash_saveis unchanged. Fixes discussion #1056. -
Linux per-argument size limit respected in auto-batching (@jdx) #1066. hk passes each rendered command to the shell as one
sh -cargument, and Linux limits each argument to 32 pages (~128 KiB) independently of the aggregateARG_MAX. Batching now caps rendered commands using both the existingARG_MAX / 2margin andMAX_ARG_STRLEN, sizes each chunk independently so later, longer paths cannot overflow a batch, and returns a clear error when even a single-file command cannot fit. Fixes discussion #1061.
Documentation
Full Changelog: v1.50.0...v1.51.0
💚 Sponsor hk
hk is maintained by @jdx, an open source developer for entire.io, the title sponsor of the jdx.dev open source tools including mise, aube, and more. Work on hk is funded by sponsorships.
If hk has sped up your pre-commit loop or made linting feel less painful, please consider sponsoring at jdx.dev. Sponsorships are what keep hk moving and the project independent.