hk Updates & Release Notes
30 updates curated from 1 source by the Releasebot Team. Last updated: Jul 22, 2026
- Jul 21, 2026
- Date parsed from source:Jul 21, 2026
- First seen by Releasebot:Jul 22, 2026
v1.52.0: Monorepo subprojects and working-tree linting
hk adds monorepo support with nested subproject configs and per-directory mise environments, plus a new --unstaged mode for linting only working-tree changes. It also brings a Markdown formatter and several correctness fixes for branch guards, empty remotes, commit scopes, and version messages.
This release brings hk to monorepos with nested subprojects configs and per-directory mise environments, adds a
--unstagedflag for linting only working-tree changes, and ships a batch of correctness fixes for branch guards, empty remotes, and conventional commit scopes.Added
subprojects for monorepos (@jdx) #1094
The root
hk.pklcan list literal directories or globs, and each subproject's ownhk.pklis merged into the root run scoped to its directory. Step working directories and glob matching are relative to the subdirectory, flat step names are prefixed with<dir>:(e.g.packages/web:eslint) for--step/skip_steps, and a subproject's env applies only to its own steps. Paired with this,HK_MISE=1now resolvesmise env --jsonper step dir, so subproject-local tools land on PATH β including for structured argv commands.// hk.pkl (repo root) subprojects = List("frontend", "packages/*") // frontend/hk.pkl hooks { ["check"] { steps { ["eslint"] = (Builtins.eslint) { batch = true } } } }--unstaged flag (@jdx) #1093
Available on
hk check,hk fix, andhk run <hook>, this selects only unstaged and untracked files (excluding staged files) without stashing β the strict inverse of--staged. It's aimed at agent-stop hooks that need to lint just the files an AI agent touched in the working tree. Conflicts with--staged,--all,--files,--from-ref/--to-ref,--glob,--pr, and--stash.rumdl_format builtin (@risu729) #1080
Adds a dedicated Markdown formatter using
rumdl fmt --check --diff / rumdl fmt, separate from the existingrumdllinter, mirroring the linter/formatter split hk already uses for ruff, taplo, and tombi.["rumdl_format"] = Builtins.rumdl_formatFixed
Branch guard allows detached HEAD (@jdx) #1075
The
no_commit_to_branchguard treatedgit symbolic-refexiting nonzero as fatal, blocking commits created during operations like interactive rebase where HEAD is detached. It now treats a detached HEAD as "not on a protected branch" while still surfacing genuine Git errors. Fixes discussion #1074.Pre-push works against an empty remote (@sshine) #1090
When a pre-push hook runs before the first push, the unresolvable base ref no longer fails the run; hk falls back to listing all files at the target ref. This is @sshine's first contribution.
hk util skips config loading (@jdx) #1078
Builtins like trailing-whitespace shell out to
hk util, and each child was loading project and user Pkl config it never used. Utility commands now run with default settings, avoiding redundant parallel evaluation that was a likely source of intermittent failures under normal concurrency. Fixes discussion #1077.mise formatter no longer batches (@risu729) #1079
The builtin had
batch = trueeven thoughmise fmtprocesses project config itself and takes no file arguments, so multiple matched files could launch duplicate whole-project jobs. It now runs once per invocation.Conventional commit scope validation (@LordAizen1) #1071
check-conventional-commitnow rejects empty scopes (feat(): ...) and malformed scopes with junk after the closing paren (feat(scope)(x): ...), which the previous check accepted. Valid forms likefeat(scope)!: ...still pass. This is @LordAizen1's first contribution.min_hk_version error message (@smasato) #1070
The running and required versions were swapped in the "version is less than the minimum required" error; the message now reports them correctly.
New Contributors
@sshine made their first contribution in #1090
@LordAizen1 made their first contribution in #1071
Full Changelog: v1.51.0...v1.52.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.
Original source - Jul 14, 2026
- Date parsed from source:Jul 14, 2026
- First seen by Releasebot:Jul 16, 2026
v1.51.0: Structured argv and disjunctive selectors
hk ships structured argv commands and match_any selectors for safer, more flexible file matching, plus a new sherif builtin for TypeScript and JavaScript monorepos. It also fixes quiet and silent output, hook argument duplication, stash lock waits, and Linux auto-batching limits.
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 argv commands (@jdx) #1067. A step's check/fix/check_list_files/check_diff can now be a Command with an explicit argv list that runs a binary via PATH without 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 shared Command rendering, and many builtins (prettier, ruff, biome, ...) have moved to structured commands. Existing string and platform Script commands are unchanged; structured commands can't be combined with shell or prefix.
check = new Command { argv = List("wc", "-c", "{{files}}") }match_any disjunctive file selectors (@risu729) #1055. Clauses compose with OR semantics; glob and types within a clause compose with AND. match_any cannot be mixed with the top-level glob or types, and empty selectors are rejected at validation time. {{globs}} and progress text reflect the combined patterns. The shellcheck and shfmt builtins 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}}" }sherif builtin (@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.json and **/pnpm-workspace.yaml; auto-suggested when "sherif" appears in package.json.
["sherif"] = Builtins.sherifFixed
--quiet and --silent really 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 set ProgressOutput::Quiet so progress lines and successful-step summaries are gone; --quiet still prints failed-step summaries (essential diagnostic), --silent prints nothing but exit code. --stats is suppressed under both. Informational println!s in init/install/migrate were routed through info! 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. For pre-push this 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_args no longer appends "$@"; legacy .git/hooks/ shims still use it via git_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.lock and sleep with bounded backoff (up to ~775ms) before running git 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; libgit2 stash_save is 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 -c argument, and Linux limits each argument to 32 pages (~128 KiB) independently of the aggregate ARG_MAX. Batching now caps rendered commands using both the existing ARG_MAX / 2 margin and MAX_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
Vertically center social link icons on the docs site (@smasato) #1059.
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.
Original source All of your release notes in one feed
Join Releasebot and get updates from jdx and hundreds of other software products.
- Jul 6, 2026
- Date parsed from source:Jul 6, 2026
- First seen by Releasebot:Jul 7, 2026
v1.50.0: Staged pre-commit, everywhere
hk ships pre-commit correctness fixes, a new textlint builtin, and a content-addressed config cache, plus documentation updates. It now keeps pre-commit scoped to staged files, handles rename-only commits correctly, and improves config and docs accuracy.
A cluster of pre-commit correctness fixes so hk run pre-commit (and installed hooks) really only see staged files, plus a textlint builtin, a content-addressed config cache, and a docs accuracy pass.
Added
textlint builtin (@smasato) #1036. Adds textlint as a batched natural-language linter for Markdown and plain text. check runs textlint {{ files }}, fix runs textlint --fix {{ files }}, and it auto-suggests when .textlintrc* or a "textlint" entry in package.json is present.
["textlint"] = Builtins.textlint
Fixed
Pre-commit is staged-only, even without stashing (@jdx) #1023. The default file selection for pre-commit steps now stays scoped to staged paths when stash = false, matching the "only what you're committing" expectation instead of also picking up unstaged and untracked matches.
Installed global pre-commit hooks pass --staged (@jdx) #1043. hk install --global now generates pre-commit hook commands as hk run pre-commit --from-hook --staged, so an env override like HK_STASH=git can't defeat a repo-level stash = "none" policy. Local config hooks and legacy .git/hooks/ shims are unchanged. Fixes discussion #1030.
Git string settings actually apply (@jdx) #1042. String, enum, and path settings from git config are now read via Config::get_string, which works on the live config that Repository::config() returns. Previously libgit2 rejected the borrow-returning read, so values like hk.stash silently fell back to defaults even though hk config explain listed git as their source. Fixes discussion #1031.
Rename-only commits no longer bypass pre-commit (@smasato) #1035. Under the default libgit2 backend, git mv commits were reporting "Fetching staged files (0 files)" and skipping every step, because git2's StatusEntry::path() returns the old path for renamed entries β which no longer exists in the worktree and got filtered out. hk now resolves the new path from the head-to-index delta for INDEX_RENAMED entries. The shell-git porcelain parser is also fixed to consume the trailing original-path field on R/C records instead of misparsing it as another status entry. Regressed since rename detection was enabled in #347.
check_first prefers check_list_files over check (@risu729) #1038. Restores the pre-#547 ordering for the fix-mode prepass: check_diff -> check_list_files -> check. For steps that define both check and check_list_files, the prepass can now narrow the subsequent fix to just the files that need writes, taking fewer write locks and keeping <JOB_FILES> scoped to files that were actually processed. Normal hk check still prefers check first β this only changes the fix-mode check_first path. Empty platform-specific scripts are also skipped when choosing the check-first command.
Performance
Resolved config cache shared by content (@jdx) #1044. When a hk.pkl (and all of its imports) resolves purely from local files, the resolved-config cache now keys off content instead of the absolute config path, so byte-identical configs in different directories share one cache entry. Configs that pull in remote http or package imports keep the previous path-keyed behavior since hk can't hash those.
Changed
tera bumped to v2 (#1028).
Documentation
Docs accuracy pass (@jdx) #1022. Audits the docs against src/env.rs, settings.toml, and pkl/Config.pkl and fixes the drift. Notably: HK_STASH default is documented as none (not git), patch-file is described as an alias of git, environment_variables.md is reorganized alphabetically with previously undocumented vars added (HK_CACHE, HK_CHECK, HK_CONFIG_DIR, HK_JSON, HK_PKL_BACKEND, HK_PKL_CA_CERTIFICATES, HK_PKL_HTTP_REWRITE, HK_STASH_BACKUP_COUNT, HK_TERMINAL_PROGRESS, HK_TRACE, HK_WALK_IGNORE, HK_WARNINGS, and the env aliases from settings.toml), pkl_introduction.md examples use the real Step class with valid pkl syntax, logging.md uses --plan (not the nonexistent --dry-run) and the correct info default level, and builtin counts move to 140+.
New vector logo (@jdx) #1041. Replaces the raster hook illustration with a stroke-based "hk" wordmark whose descender curls into a fishing hook, plus a bare hook glyph for the navbar and favicons. Scales cleanly down to 16px and to monochrome.
Full Changelog: v1.49.0...v1.50.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.
Original source - Jul 1, 2026
- Date parsed from source:Jul 1, 2026
- First seen by Releasebot:Jul 3, 2026
v1.49.0: More builtins, a stash tail-deletion fix
hk adds three new builtin linters, tighter text-only file matching, and a three-way merge fix that preserves fixer deletions during overlapping edits. It also brings a faster RuboCop server mode, a new shellharden builtin, broader ryl config detection, and updated bundled tools.
Three new builtin linters, tighter file matching for text-only hooks, and a three-way merge fix that stops fixer tail deletions from being dropped when a worktree edit overlaps their start.
Added
rubocop_server builtin (@andyw8) #995. A sibling of Builtins.rubocop that passes --server to check, check_list_files, and fix, running RuboCop as a long-lived daemon for much faster repeat invocations.
["rubocop"] = Builtins.rubocop_servershellharden builtin (@hituzi-no-sippo) #996. Adds shellharden as a batched bash/sh linter. check runs shellharden --check (exit code 2 on suggested changes); fix runs shellharden --replace. Auto-suggested when *.sh or *.bash files are present.
More ryl project indicators (@hituzi-no-sippo) #998. The ryl builtin now also discovers configs at .config/.ryl.toml, .config/ryl.toml, and pyproject.toml (when it contains a [tool.ryl] table), matching ryl's own upward search. The bundled ryl tool stub bumps to 0.20.0.
Changed
Text-only builtins use types = List("text") (@hituzi-no-sippo) #997. Replaces glob = "**/*" with a text-type filter for builtins that only make sense on text files: byte_order_marker, check_executables_have_shebangs, check_merge_conflict, detect_private_key, dprint, fix_smart_quotes, mixed_line_ending, newlines, trailing_whitespace, and typos. check_added_large_files, check_case_conflict, and check_symlinks intentionally keep the everything-matcher since they're meaningful for binaries too.
pklr bumped to 1.1.1 (@jdx) #1018, and Renovate no longer waits out the shared release-age delay for pklr updates (#1017), so fixes in the embedded pkl evaluator land in hk sooner.
itertools updated to 0.15 (#1014).
Fixed
Fixer tail deletions no longer get dropped when a worktree edit overlaps the start (@jdx) #990. In the three-way merge, a pure-deletion fixer hunk whose start was consumed by a worktree hunk was skipped entirely, causing trailing lines the fixer wanted to remove to reappear in the merged output. three_way_merge_hunks now trims such hunks to the current index and keeps the unconsumed tail. Includes a regression test for discussion #988. If you saw stash = "patch-file" restoring lines a fixer had deleted, this is the fix.
betterleaks builtin test uses a still-detected fixture (@risu729) #1006. Since betterleaks 1.2.0, aws-access-token is a composite rule that requires a nearby secret key, so the old AWS-only fixture no longer failed the scanner. The test now uses a github-pat sample that still trips β₯1.2.0, and the bundled betterleaks stub moves to 1.5.0.
Documentation
Link to all sponsors (@jdx) #991.
Clarify contribution fit in the contributing guide (@jdx) #992.
New Contributors
@risu729 made their first contribution in #1006
@andyw8 made their first contribution in #995
Full Changelog: v1.48.0...v1.49.0
π Sponsor hk
hk is developed by @jdx at en.dev β a small independent studio behind developer tools like mise, aube, hk, 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 en.dev. Sponsorships are what keep hk moving and the project independent.
Original source - Jun 11, 2026
- Date parsed from source:Jun 11, 2026
- First seen by Releasebot:Jun 16, 2026
v1.48.0: Group inheritance and builtin polish
hk adds inherited group defaults for child steps, a new aqua checksum builtin, broader RuboCop file matching, and a pklr validation fix for inline group configs, making lint and hook setup smoother and more consistent.
Groups can now define defaults that child steps inherit, plus a new aqua checksum builtin, expanded RuboCop file matching, and a pklr fix for inline group configs.
Added
Inherit step settings from groups (@RobertDeRose) #982. A Group can now set dir, prefix, workspace_indicator, shell, stage, and exclude, and child steps inherit any field they don't define themselves. Override semantics are simple: a child value fully replaces the group value, never merges.
local frontend = new Group { dir = "packages/frontend" prefix = "mise x --" steps { ["prettier"] = (Builtins.prettier) { batch = true } ["eslint"] = (Builtins.eslint) { dir = "different/path" // overrides the group dir batch = true // still inherits prefix } } }aqua_update_checksum builtin (@hituzi-no-sippo) #977. A fix-only step that runs aqua update-checksum --prune whenever your aqua config or checksum files change, keeping aqua-checksums.json up to date and pruning unused entries.
Changed
ryl and ryl_markdown switch to check_diff (@hituzi-no-sippo) #978. Both builtins now run ryl --diff for check, surfacing exactly which YAML edits the linter wants to make. They also pick up project indicators (ryl.toml, .ryl.toml, .yamllint*) so the builtins are auto-suggested, and the bundled ryl tool stub moves to 0.15.0.
RuboCop builtin file filter mirrors RuboCop's defaults (@hituzi-no-sippo) #969. Replaces the types = List("ruby") matcher with the explicit glob list from RuboCop 1.87.0's default config, covering .rb, .gemspec, .rake, Gemfile, Rakefile, Vagrantfile, and friends, and applies RuboCop's default excludes (node_modules/, tmp/, vendor/, .git/). The bundled rubocop stub bumps to 1.87.0.
Fixed
pklr validation of inline new Group entries (@jdx) #983. Bumps pklr to 1.0.6, which fixes validation of Mapping<String, Step | Group> properties initialized with new Mapping<String, Step> {} β the shape used by hk's default hook config. Configs with inline new Group { ... } step entries no longer fail under the default pklr backend. Refs #981.
New Contributors
@RobertDeRose made their first contribution in #982
Full Changelog: v1.47.0...v1.48.0
π Sponsor hk
hk is developed by @jdx at en.dev β a small independent studio behind developer tools like mise, aube, hk, 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 en.dev. Sponsorships are what keep hk moving and the project independent.
Original source Similar to hk with recent updates:
- Claude Code updates403 release notes Β· Latest Jul 22, 2026
- Claude updates116 release notes Β· Latest Jul 14, 2026
- Firefox updates36 release notes Β· Latest Jul 21, 2026
- Safari updates22 release notes Β· Latest Jun 8, 2026
- iOS updates26 release notes Β· Latest Jun 9, 2026
- macOS updates26 release notes Β· Latest Jun 9, 2026
- Jun 9, 2026
- Date parsed from source:Jun 9, 2026
- First seen by Releasebot:Jun 9, 2026
v1.47.0: pklr by default, sturdier stash restores
hk adds the embedded pklr config backend by default, removing the need for the pkl CLI, and improves reliability with stash, merge-base, and Windows batching fixes. It also adds new builtins like ryl, ryl_markdown, hk_test, and a sponsor command.
hk now ships with the built-in pklr evaluator as the default config backend β no pkl CLI required β plus three stash, merge-base, and Windows batching fixes that close out reported regressions, and a handful of builtin improvements from @hituzi-no-sippo.
Added
pklr is now the default pkl backend (@jdx) #976. hk.pkl is evaluated with the embedded pklr interpreter out of the box, so the Apple pkl CLI is no longer required to use hk. The CLI backend is still available via HK_PKL_BACKEND=pkl; unrecognized values now warn and fall through to pklr. The config cache also switched from mtime comparisons to hashing file contents, so edits to imported .pkl files reliably invalidate the cache.
# Default (no setup required) hk check # Opt back into the pkl CLI HK_PKL_BACKEND=pkl hk checkryl builtin gains fix and check_list_files (@hituzi-no-sippo) #967. Bumps the underlying ryl to v0.13.0 and wires in the new commands. The yamllint config dependency is dropped.
ryl_markdown builtin (@hituzi-no-sippo) #968. Lints YAML embedded inside Markdown documents using ryl's markdown support.
hk_test builtin (@hituzi-no-sippo) #973. Runs hk test --quiet whenever your hk configuration file changes so step-defined inline tests catch regressions automatically.
hk sponsors command (@jdx) #961. A small no-config subcommand that prints the projects and companies sponsoring hk and the en.dev project family. Works without hk.pkl.
Fixed
Last-line edits of partially-staged files no longer get corrupted on restore (@ad1269) #966. The "pure tail insertion" special case in the manual stash restore had a newline-tolerant fallback that stripped the index snapshot's trailing newline before the prefix check, so a last-line edit like l3: tail β l3: tail UNSTAGED was misclassified as a tail insertion and re-emitted as fixer content + " UNSTAGED\n". The fallback now only accepts an empty remainder (the original case from #304); real last-line edits fall through to the three-way merge, which handles them correctly. The recovery patch written under the state dir also restores the trailing newline that cmd.read() strips, so git apply --check no longer fails with corrupt patch at line N. Fixes #965.
hk check works when there is no merge base (@jdx) #975. files_between_refs previously bailed when libgit2 or git couldn't find a common ancestor (e.g. shallow clones or unrelated histories). It now falls back to a shell git merge-base, then to a direct from..to tree/shell diff. Both the libgit2 and shell-git paths use the same range logic, covered by new bats tests with HK_LIBGIT2=1 and HK_LIBGIT2=0. Refs #972.
Auto-batching respects the cmd.exe command-line limit (@jdx) #974. auto_batch_jobs now selects a shell-specific safe length: 4095 bytes (half of Windows' ~8191-character cap) for cmd.exe, and ARG_MAX / 2 for everything else. Medium-sized {{files}} expansions that fit under Unix ARG_MAX no longer blow past the cmd limit unbatched. Fixes #971.
git2 updated to 0.21 (#956).
Remove singular sponsor link from docs footer (@jdx) #962.
Documentation
Add a sponsor footer to the docs site (@jdx) #960.
Full Changelog: v1.46.0...v1.47.0
π Sponsor hk
hk is developed by @jdx at en.dev β a small independent studio behind developer tools like mise, aube, hk, 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 en.dev. Sponsorships are what keep hk moving and the project independent.
Original source - May 27, 2026
- Date parsed from source:May 27, 2026
- First seen by Releasebot:May 28, 2026
v1.46.0: --staged scope, global install, and a stash trilogy
hk releases a feature-and-fix update with staged-file hook runs, smoother global installs, new named post-checkout variables, and built-in support for oxfmt, Vite+ and improved oxlint. It also fixes several stash, merge and pre-push bugs for safer hook behavior.
A feature-and-fix release: hooks can now target staged files without touching your worktree, hk install cooperates with global installs, and three separate stash/merge bugs that could clobber fixer output or staged deletions are fixed.
Added
--staged flag for hk run, check, fix, and hook subcommands (@jdx) #950. Runs hooks against the staged file set while leaving unstaged and untracked changes alone β no stash, no worktree mutation. It conflicts with --all and --stash, and forces StashMethod::None even when the hook config opts into stashing. Fixes #940.
hk run pre-commit --staged hk fix --stagedhk install skips when hk is configured globally (@jdx) #934. If any hook.hk-* entry exists in ~/.gitconfig, hk install is a no-op and additionally cleans up stale per-repo hooks left behind from a prior install, so the global install is the single source of truth and hk doesn't fire twice per event. Pass --force-local to install per-repo hooks anyway. This means postinstall workarounds like git config --get-regexp hook.hk- || hk install can now be replaced with a plain hk install. Closes #933.
Named template variables for post-checkout hooks (@jdx) #951. Steps can now reference prev_head, new_head, and is_branch_checkout (a real boolean, mapped from git's 1/0 flag) instead of having to parse the combined hook_args string. docs/hooks.md documents the per-hook variables for prepare-commit-msg, commit-msg, and post-checkout.
oxfmt builtin (@hituzi-no-sippo) #914. Adds oxfmt as a builtin formatter for JS/TS, JSON, YAML, and TOML.
Vite+ builtin (@hituzi-no-sippo) #913. Adds Vite+ as a builtin formatter/linter for JavaScript/TypeScript.
oxlint builtin upgrades (@hituzi-no-sippo) #911. Adds --deny-warnings so violations exit non-zero, extends the file glob to .vue, .svelte, .astro, .mjs, .cjs, .mts, and .cts, and registers oxlint config files as project indicators so the builtin is auto-suggested.
Fixed
pre-push ref filter was inverted (@jdx) #932. The filter was checking the local sha for all-zeros (a deletion) when the intent was to check the remote sha (a new branch). Two visible consequences:
First push of a new branch was dropped and fell through to resolving refs/remotes/origin/HEAD, which often failed with Failed to parse reference: refs/remotes/origin/HEAD (likely the root cause of #172).
Branch deletions were kept and triggered linting against the deleted ref.
The filter now drops only deletions, falls back to the real remote-tracking branch (or Git::resolve_default_branch()) for new-branch pushes, and uses a new git::is_zero_sha() helper that works for both SHA-1 and SHA-256 repos.
hk install --global now uses absolute paths (@jdx) #939. Global hook commands previously assumed hk/mise were on PATH when git invoked the hook, which broke in environments with a sanitized PATH. The installer now resolves hk (or mise) to an absolute path at install time (using ~/ when home-relative and quoting otherwise), and --mise global installs use mise x hk -- hk so the hk tool is requested explicitly. Global installs also pick hook events from the project's hk.pkl when present. Fixes #937.
fail_on_fix no longer loses fixer output through stash = "git" (@jdx) #909. git stash show --name-only can list staged files stored in the stash commit that were not part of the unstaged set being restored, so the manual unstash could rewrite a staged-only file and discard the fixer's output that should remain visible as an unstaged diff. hk now tracks the exact path set selected for stashing and filters restore to that set. Follow-up to the fail_on_fix fix in v1.44.3.
Staged deletions survive pop_stash (@jdx) #927. pop_stash() walked every path returned by git stash show --name-only and wrote a merged blob to disk, even for paths the user had staged for deletion with git rm. After the commit, the deleted file reappeared on disk as untracked. hk now queries git diff --cached --diff-filter=D before unstashing and skips those paths. Fixes #926.
Fixer tail-line deletions are preserved across three-way merge (@jdx) #931. In merge.rs::diff_hunks, when the LCS walk consumed other entirely after a matching line, a pure tail deletion of base[i..n] was dropped, so three_way_merge_hunks silently copied the removed lines back in. The classic symptom: a fixer that strips trailing blank lines, applied to a file where you have an unrelated unstaged change in the middle, would have its trailing-line cleanup silently undone. Fixes #929.
check_diff failures get accurate fix suggestions (@jdx) #949. When a step defined both check_diff and check_list_files, the "To fix, run" hint always parsed output with the list-files parser regardless of which check actually ran. hk now passes the executed command into collect_fix_suggestion and dispatches to the diff parser for check_diff output, so the suggested files match the real failure. Fixes #942.
Full Changelog: v1.45.0...v1.46.0
π Sponsor hk
hk is developed by @jdx at en.dev β a small independent studio behind developer tools like mise, aube, hk, 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 en.dev. Sponsorships are what keep hk moving and the project independent.
Original source - May 5, 2026
- Date parsed from source:May 5, 2026
- First seen by Releasebot:May 6, 2026
v1.45.0: Buildifier built-ins and smarter auto-batching
hk adds first-class Bazel buildifier built-ins and improves auto-batching so steps are only split when the rendered command actually needs file-list batching, reducing unnecessary job fan-out and preserving step settings across batches.
Added
buildifier_format and buildifier_lint built-ins (@plx) #896. Two new built-ins for Bazel projects, modeled on buf_format / buf_lint. They cover BUILD, BUILD.bazel, WORKSPACE, WORKSPACE.bazel, MODULE.bazel, *.bzl, *.star, and *.sky files, and ship with the usual project-indicator metadata so they're auto-suggested for Bazel repos.
import "package://github.com/jdx/hk/releases/download/v1.45.0/[email protected]#/Builtins.pkl" hooks { ["pre-commit"] { steps = new { ["buildifier-format"] = Builtins.buildifier_format ["buildifier-lint"] = Builtins.buildifier_lint } } }Fixed
Auto-batching no longer splits jobs whose command doesn't reference {{files}} (@jdx) #901. Previously, hk decided whether to split a step into multiple ARG_MAX-safe batches purely from the size of the file-list expansion. On Windows β where ARG_MAX falls back to 128KB β a step like:
local vscodeCommitHint = new Step { exclusive = true check = "echo If you see this message in a pop-up, the pre-commit steps failed." }β¦against a ~20K-file repo would be fanned out into ~29 jobs, printing the message 29 times even though the command never used {{files}}. Auto-batching now happens at execution time with the full tera context available, renders the real run command for each candidate batch, and only splits when the rendered command exceeds the safe limit. Byte estimation is kept as a fallback if rendering fails. The split path also now correctly preserves check_first and workspace_indicator across batches (the old code dropped them with a TODO).
Documentation
The README now credits Namespace for providing CI runners for hk (@jdx) #895.
New Contributors
@plx made their first contribution in #896
Full Changelog: v1.44.3...v1.45.0
π Sponsor hk
hk is developed by @jdx at en.dev β a small independent studio behind developer tools like mise, aube, hk, 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 en.dev. Sponsorships are what keep hk moving and the project independent.
Original source - Apr 30, 2026
- Date parsed from source:Apr 30, 2026
- First seen by Releasebot:May 1, 2026
v1.44.3: Honest fail_on_fix and readable CI logs
hk ships a small patch release that fixes fail_on_fix handling so staged changes stay visible for review, and makes text-mode progress output readable in CI logs again with cleaner, quieter status updates.
A small patch release fixing two notable rough edges: fail_on_fix=true no longer silently re-stages the fixer's output over your git add, and hk's text-mode progress output is finally readable in CI logs.
Fixed
fail_on_fix=true no longer overwrites your staged changes with the fix (@jdx) #892. Previously, when a hook had fail_on_fix = true, the step's auto-staging would silently merge the fixer's output into the index over your explicit git add choices. After the failed commit, the fix was no longer visible as an unstaged change for review, and a re-commit would silently succeed with the fix baked in β defeating the entire point of fail_on_fix. should_stage is now forced off for RunType::Fix runs when fail_on_fix is set, so the fixer's output stays in the worktree as an unstaged change for you to inspect, and the commit keeps failing until you accept it. Fixes #888.
Text-mode progress output is usable in CI again (@jdx) #890. hk's output in GitHub Actions and other piped-stderr environments was a mess: raw [9A[80D[0J cursor-control escapes leaked into the log, every status change was duplicated, failure stderr was suppressed, and a step matching hundreds of files dumped ~4KB of paths into every progress line. This release fixes the lot:
- Bumps clx to 2.0.1, which makes refresh_once() a no-op in text mode (no more leaked UI escape codes) and dedupes consecutive identical job lines per job.
- Failure summaries are now emitted in text mode by default. Successful steps stay quiet (their output already streamed during execution), but failed steps get a full diagnostic block at the end so you can see the failure in one place. HK_SUMMARY_TEXT=1 still forces every step's summary to print.
- Per-step progress messages are bounded. A new display-only tera context truncates files / workspace_files to first_file β¦ when more than one file matches, and the rendered message itself is capped at 2048 printable chars (ANSI-aware). The execution command is rendered against the full file list as before β only the human-readable progress line is truncated.
- Stops truncating text-mode messages at 60 chars. The previous truncate_text filter clamped to term_width - 20, which is 60 in non-TTY environments β exactly enough to hide the diagnostic detail you actually need to debug a CI failure.
A typical dbg step matching 98 .rs files now reads:
dbg β 98 files β **/*.rs β ! rg -e 'dbg!' bin/generate_docs.rs β¦instead of unrolling all 98 paths on every prop update.
Full Changelog: v1.44.2...v1.44.3
π Sponsor hk
hk is developed by @jdx at en.dev β a small independent studio behind developer tools like mise, aube, hk, 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 en.dev. Sponsorships are what keep hk moving and the project independent.
Original source - Apr 26, 2026
- Date parsed from source:Apr 26, 2026
- First seen by Releasebot:Apr 27, 2026
v1.44.2: pklr cache freshness and quieter Builtins
hk ships a patch release that fixes HK_PKL_BACKEND=pklr rough edges, so hk.pkl edits are picked up immediately and Builtins.pkl no longer spams deprecation warnings. It also improves mobile docs layout and adds release version and GitHub star info to the VitePress site nav.
A small patch release focused on two HK_PKL_BACKEND=pklr rough edges: edits to hk.pkl are now picked up immediately, and loading Builtins.pkl no longer spams deprecation warnings on every run.
Fixed
hk.pkl edits are now picked up under HK_PKL_BACKEND=pklr (@jdx) #879. The two pkl backends return different things from analyze_imports β the pkl CLI happens to include the source file in resolvedImports, but pklr only returns transitive import URIs. As a result, with pklr the main hk.pkl was missing from the config cache's fresh_files, so edits didn't invalidate the cache and hk kept reusing the stale Config until you ran hk cache clear. The main config path is now always added to fresh_files. Fixes #877.
No more pklr deprecation warnings on every Builtins.pkl load (@jdx) #880. Previously, every invocation under HK_PKL_BACKEND=pklr printed:
[pklr] WARNING: property 'check_byte_order_marker' is deprecated [pklr] WARNING: property 'fix_byte_order_marker' is deprecatedβ¦even when your hk.pkl didn't reference those aliases. This release bumps pklr to 0.4.2 (which evaluates @Deprecated lazily, on field access) and reworks Builtins.pkl so its own internal bindings no longer touch the deprecated aliases at load time. The migration nudge still fires if you explicitly reference Builtins.check_byte_order_marker or Builtins.fix_byte_order_marker. Fixes #878.
Mobile docs banner layout (@jdx) #865, #867. At <=640px, the banner now stacks the message and "Read more" link vertically, with the close button pinned to the top-right corner instead of floating in the middle of the taller stacked layout.
Documentation
The VitePress site nav now surfaces the current release version (parsed from Cargo.toml) and a GitHub star counter, matching the mise and aube docs (@jdx) #872.
Full Changelog: v1.44.1...v1.44.2
π Sponsor hk
hk is developed by @jdx at en.dev β a small independent studio behind developer tools like mise, aube, hk, 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 en.dev. Sponsorships are what keep hk moving and the project independent.
Original source - Apr 24, 2026
- Date parsed from source:Apr 24, 2026
- First seen by Releasebot:Apr 25, 2026
v1.44.1: post-commit / pre-rebase and faster YADM-style worktrees
hk ships a small patch release that fixes post-commit and pre-rebase as proper hk run subcommands and makes HK_STASH_UNTRACKED=false skip the untracked-file scan, improving performance for large worktrees and YADM-style dotfile repos.
A small patch release fixing two rough edges introduced with the v1.44.0 global-hooks work: post-commit and pre-rebase now have proper hk run subcommands, and HK_STASH_UNTRACKED=false finally skips the untracked-file scan (not just the stash), which makes hk usable on YADM-style dotfile repos where GIT_WORK_TREE is $HOME.
Fixed
hk run post-commit and hk run pre-rebase are now first-class subcommands (@jdx) #858. Both events are written by hk install, but previously fell through to the generic other handler β so they didn't show up in hk run --help and their arguments got mixed into the positional file collector, occasionally producing confusing Usage: hk run --from-ref <FROM_REF> [FILES]... errors during git rebase. pre-rebase now has a typed <upstream> [branch] signature matching git's spec, and post-commit is a proper no-args handler.
HK_STASH_UNTRACKED=false now also skips the untracked scan in git status (@jdx) #861. Before this, the flag only suppressed stashing β hk still ran git status --untracked-files=all on every invocation, which could take tens of seconds and emit hundreds of megabytes of output when GIT_WORK_TREE points at a large directory like $HOME (as in YADM). Both the libgit2 and shell-git code paths now honor the setting, so large-worktree users can opt out of the scan entirely. Fixes #860.
export HK_STASH_UNTRACKED=false hk check --all # no longer scans the entire worktree for untracked filesDocumentation
Getting-started docs now lead with hk install --global as the recommended setup path, since the --from-hook short-circuit added in v1.44.0 makes it safe to enable once per machine (@jdx) #855.
Added a dismissible cross-site announcement banner to hk.jdx.dev, with an optional expires field so banners auto-hide on their own (@jdx) #857, #862.
Full Changelog: v1.44.0...v1.44.1
π Sponsor hk
hk is developed by @jdx at en.dev β a small independent studio behind developer tools like mise, aube, hk, 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 en.dev. Sponsorships are what keep hk moving and the project independent.
Original source - Apr 23, 2026
- Date parsed from source:Apr 23, 2026
- First seen by Releasebot:Apr 24, 2026
v1.44.0: Install Globally, Plan Before You Run
hk releases smarter hook control with dry-run planning, detailed skip reasons, and JSON output, plus global hook installs for every repo on Git 2.54+. It also adds bare-repo dotfile support, a new cocogitto_commit_msg Conventional Commits builtin, and CI text progress fixes.
Highlights
This release is all about understanding and controlling where hk runs. hk check --plan lets you dry-run a hook and see exactly which steps would execute and why, hk install --global registers hk against every repo on your machine using Git 2.54's new config-based hooks, and bare-repo dotfile managers like YADM are now supported via GIT_DIR/GIT_WORK_TREE.
- hk check --plan / --why / --json β dry-run any hook to see which steps would run, which would skip, and why, with JSON output for tooling
- hk install --global β install hooks once in ~/.gitconfig and have hk apply to every repo (Git 2.54+)
- Bare-repo dotfile support β hk now respects GIT_DIR and GIT_WORK_TREE, so YADM and similar setups work out of the box
- New cocogitto_commit_msg builtin for Conventional Commits validation
Added
hk check --plan / -P, --why [STEP] / -W, --json / -J
You can now dry-run a hook to see what hk would do without executing any commands. --plan prints the parallel groups, matched file counts, and included/skipped steps with reasons; --why drills into the skip reasons for every step (or a specific one); --json emits the plan as structured JSON for tooling. (@jdx) #848
$ hk check --plan Plan: check Run type: check [parallel group] group_0 β actionlint (no files matched filters) β cargo-fmt (6 files matched) β cargo-clippy (required profile(s) not enabled: slow) β cargo-check (6 files matched)The planner reuses hk's real job-building and skip-evaluation logic, so the plan accurately reflects what would happen β including filter matches, profile gating, condition evaluation, dependsOn, and --step/--skip-step selections. It never executes step commands.
Git 2.54 config-based hook installation with --global
On Git 2.54+, hk install now writes config-based hooks (hook.hk-<event>.command / .event) instead of shell shims in .git/hooks/. The hooks directory is left untouched, and hk composes cleanly with other hook managers. Use --legacy to force the old shim behavior; older Git falls back automatically. (@jdx) #853
More importantly, hk install --global writes those entries to your ~/.gitconfig so hk runs in every repository on your machine:
$ hk install --globalIn repos without an hk.pkl (or without a matching event), the invocation is a silent no-op via a new hidden hk run --from-hook flag β install once, forget, and repos that don't use hk are unaffected. hk uninstall now cleans up both script shims and config entries regardless of current Git version, and hk uninstall --global removes the global entries.
GIT_DIR / GIT_WORK_TREE support for bare-repo dotfile managers
hk now honors these environment variables during repository discovery, so it works with YADM and similar bare-repo dotfile setups where there is no .git directory in the work tree. When libgit2 opens a bare repo, hk falls back to shell git for status/diff operations (libgit2 refuses those on bare repos). As a bonus, hk builtins no longer loads project settings, so it runs outside a repo instead of panicking. Fixes #831. (@jdx) #847
cocogitto_commit_msg builtin
A new builtin linter that validates commit messages against the Conventional Commits spec using cocogitto's cog verify. Uses the {{commit_msg_file}} template variable, making it a drop-in for the commit-msg hook. (@hituzi-no-sippo) #838
Fixed
Text progress in CI
Some CI systems allocate a pseudo-TTY, which made console::user_attended_stderr() report an interactive stderr while the log collector stripped cursor-control escapes and recorded spinner frames as noisy log rows. hk now detects CI environments via is_ci and forces clx progress into text mode, while leaving local interactive behavior unchanged. (@jdx) #845
Changed
Bumped communique to 1.0.1 (#850) and updated clx to v2 (#836).
Full Changelog: v1.43.0...v1.44.0
π Sponsor hk
hk is developed by @jdx at en.dev β a small independent studio behind developer tools like mise, aube, hk, 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 en.dev. Sponsorships are what keep hk moving and the project independent.
Original source - Apr 16, 2026
- Date parsed from source:Apr 16, 2026
- First seen by Releasebot:Apr 17, 2026
v1.43.0: Stdin forwarding, harper builtin, and musl binaries
hk adds {{ hook_stdin }} to forward git hook stdin into step commands, completing git-lfs pre-push support. It also introduces a built-in Harper grammar checker, ships Linux musl binaries for Alpine-based systems, and returns to crates.io for easy cargo install.
This release adds {{ hook_stdin }} for forwarding git hook stdin to step commands (completing git-lfs support started in v1.42.0), introduces a built-in harper grammar checker, and ships Linux musl binaries for Alpine and other musl-based distributions.
Highlights
- {{ hook_stdin }} template variable completes git-lfs pre-push support -- LFS objects are now properly uploaded during git push
- harper-cli builtin adds grammar checking as a first-class linter
- Linux musl release binaries for Alpine and other musl-based distros
- hk is back on crates.io -- installable via cargo install hk again
Added
{{ hook_stdin }} template variable: Step commands can now receive the raw stdin that git passes to hook scripts via the stdin field. This is essential for git lfs pre-push, which needs the ref data piped through stdin to know which LFS objects to upload. Without this, git lfs pre-push would silently succeed but upload nothing, causing the remote to reject the push. The variable is available in pre-push and post-rewrite hooks. (@JohanLorenzo) #825
hooks { ["pre-push"] { steps { ["git-lfs"] { check = "git lfs pre-push {{ hook_args }}" stdin = "{{ hook_stdin }}" } } } }For pre-push, {{ hook_stdin }} contains the ref lines that git pipes in (e.g., refs/heads/main <local-sha> refs/heads/main <remote-sha>). For post-rewrite, it contains the old/new SHA mapping lines. When stdin is a terminal (no piped data), it expands to an empty string.
Built-in harper and harper_commit_message linter steps: harper-cli is now available as a builtin linter for grammar checking prose and documentation. The harper step runs against text files, while harper_commit_message checks commit messages. (@hituzi-no-sippo) #714
Linux musl release binaries: Pre-built binaries for x86_64-unknown-linux-musl and aarch64-unknown-linux-musl are now included in releases, making hk easy to install on Alpine Linux and other musl-based distributions. (@jdx) #829
hk is published to crates.io again: The crate had been stuck at v1.10.1 since August 2025 after the publish step was accidentally dropped during a build system migration. Starting with this release, cargo install hk will get the latest version. (@jdx) #830
New Contributors
@hituzi-no-sippo made their first contribution in #714
Full Changelog: v1.42.0...v1.43.0
Original source - Apr 12, 2026
- Date parsed from source:Apr 12, 2026
- First seen by Releasebot:Apr 12, 2026
v1.42.0: Hook args template and Windows quoting fix
hk releases new hook argument support with a {{ hook_args }} template variable and dedicated post-checkout, post-merge, and post-rewrite commands, while also fixing a Windows {{files}} expansion bug that could break file-based checks.
This release adds a new
{{ hook_args }}template variable for forwarding git hook arguments to downstream commands, and fixes a Windows-specific bug where{{files}}expansion silently broke file-based checks.Added
{{ hook_args }}template variable: Step commands can now access the arguments that git passes to hook scripts via{{ hook_args }}. This is essential for tools like git-lfs, whose hooks (post-checkout, post-merge, pre-push) require the original positional arguments from git to function correctly. Without this, commands likegit lfs post-checkoutwould fail with "This should be run through Git's post-checkout hook." (@JohanLorenzo) #807hooks { ["post-checkout"] { steps { ["git-lfs"] { check = "git lfs post-checkout {{ hook_args }}" } } } ["post-merge"] { steps { ["git-lfs"] { check = "git lfs post-merge {{ hook_args }}" } } } ["pre-push"] { steps { ["git-lfs"] { check = "git lfs pre-push {{ hook_args }}" } } } }The variable is populated for all hook types: pre-push gets <remote-name> <remote-url>, commit-msg gets the message file path, post-checkout gets <prev-head> <new-head> <is-branch>, and so on. For hooks that receive no arguments (like pre-commit), it expands to an empty string.
First-class post-checkout, post-merge, and post-rewrite hooks: These three hook types now have dedicated subcommands (
hk run post-checkout,hk run post-merge,hk run post-rewrite) with proper argument parsing, rather than being handled as generic hooks. (@JohanLorenzo) #807Fixed
{{files}}expansion on Windows no longer silently breaks checks: On Windows, Rust'sCommand::argapplies MSVCRT-style argv escaping that collides withcmd.exe's own quoting rules. This caused the already-quoted{{files}}payload to reach tools with literal " characters embedded in arguments. Tools like ruff, biome, and others would silently exit 0 while processing zero files, making hk report success on broken invocations. The fix switches the Windowscmd.exe /ccode path to useraw_arg, passing the rendered command string verbatim socmd.execan parse its own quoting without Rust interference. This also affects{{workspace_files}}. (@jdx) #824New Contributors
@JohanLorenzo made their first contribution in #807
Full Changelog: v1.41.1...v1.42.0
Original source - Apr 10, 2026
- Date parsed from source:Apr 10, 2026
- First seen by Releasebot:Apr 10, 2026
v1.41.1: Cleaner hook failure output
hk ships a patch release that improves hook failure output, removing duplicate diagnostics and making failed steps show combined stdout and stderr without losing key details. It also preserves the configured summary label and keeps check_first diagnostics visible when fail_fast cancels later steps.
A patch release focused on fixing hook failure output. Previously, failing steps could produce duplicated or missing diagnostic output depending on the combination of output_summary, check_first, and fail_fast settings. These fixes ensure that failure output is shown exactly once, includes both stdout and stderr so no diagnostics are lost, and preserves the configured summary label.
Fixed
No more duplicate output on failure: The end-of-run error handler was reprinting the first failing step's output after the per-step summary had already displayed it. This caused confusing duplication and misattribution -- for example, one tool's errors could appear to be part of another tool's section. The redundant handle_script_failed output has been removed. (@nkakouros) #784
Combined output shown for failed steps: When a step fails, hk now shows combined stdout+stderr output regardless of the output_summary setting (unless set to "hide"). Previously, if output_summary was set to "stderr" but the tool wrote diagnostics to stdout (as eslint, flake8, prettier, and many others do), those diagnostics were invisible in the failure summary. (@nkakouros) #772
Configured output summary label preserved on failure: The combined-output-on-failure fix from #772 was changing the summary header from the configured label (e.g., lint stderr:) to lint combined:. The label now matches the configured output_summary value while still using combined content underneath. (@jdx) #808
check_first diagnostics preserved when cancelled by fail_fast: For steps using check_diff or check_list_files with check_first, diagnostic output from the check phase was lost if another step failed first and triggered fail_fast cancellation before the fix phase could run. The check output is now saved so it appears in the summary. (@nkakouros) #784
New Contributors
- @jhult made their first contribution in #805
Full Changelog: v1.41.0...v1.41.1
Original source
Curated by the Releasebot team
Releasebot is an aggregator of official product update announcements from hundreds of software vendors and thousands of sources.
Our editorial process involves the manual review and audit of release notes procured with the help of automated systems.