Skip to content

security(step): refuse check_diff patches that write inside .git - #1585

Merged
jdx merged 3 commits into
mainfrom
security/check-diff-reject-dot-git
Oct 3, 2026
Merged

jdx merged 3 commits into
mainfrom
security/check-diff-reject-dot-git

Conversation

@jdx

@jdx jdx commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

https://entire.io/gh/jdx/hk/trails/162

When a step defines check_diff, hk applies the patch it prints in-process instead of running the fixer. That applier (built on diffy) accepted patches that name paths inside .git, which git apply refuses. A check_diff tool that printed (or was tricked into printing) +++ .git/hooks/post-commit could therefore plant a hook that runs on the next commit, or edit .git/config.

What changes

  • Patches that name any path component git treats as its own directory are refused as a whole, before anything is written. The check mirrors git's verify_path: .git in any case, with trailing dots or spaces (.git.), NTFS alternate data streams (.git::$INDEX_ALLOCATION), the NTFS short name git~1, and .git with HFS+-ignorable Unicode inserted. Both sides of a ---/+++ pair are checked, in any directory depth (sub/.git/config). Look-alikes such as .gitignore and .github still apply.
  • A refused patch behaves like any patch that does not apply: hk warns and runs the step's fixer instead.
  • hk now creates its state directory (command output, saved stash patches, logs) with mode 0700 on Unix. Directories above it, such as ~/.local/state, are created as usual, and an existing state directory keeps its permissions.

Before and after, with a check_diff that prints a patch creating .git/hooks/post-commit next to a valid change:

before: the in-process applier writes .git/hooks/post-commit
after:  fmt: check_diff printed a patch that doesn't apply, so the fixer ran instead:
        .git/hooks/post-commit: patches may not touch git's own directory

Validation

  • cargo test: 525 passed, 0 failed (new unit tests cover the is_dot_git spellings, both-sides refusal, nothing written on refusal, and look-alikes still applying).
  • test/apply_check_diff.bats: 41 ok of a 41-test plan, for both the libgit2 and no-libgit2 runs. The new test fails when the check is disabled (I temporarily disabled it to confirm) and passes with it.
  • test/state_dir_permissions.bats (new): 1 of 1 ok; the state directory is drwx------ and its parent keeps default permissions.
  • scripts/validate-apple-pkl.py passes. hk check --all: all steps pass except cargo-check, which fails on this machine because of existing manifest warnings (package.homepage redundant, unused tracing-error) unrelated to this change.

Limits

Existing state directories are not tightened. Windows has no 0700 equivalent here, and the .git checks are written for all platforms but were only run on Linux. Other path-based risks, such as symlinks and hard links, were already refused before this change.

AI-assisted — Tool: Claude Code; model: anthropic/claude-sonnet-5-5; version: unavailable.

🤖 Generated with Claude Code


Note

High Risk
Changes security-sensitive patch application and how locally stored hook output/stash data is created on disk; incorrect matching could block legitimate diffs or still allow .git writes.

Overview
Closes a gap where the in-process check_diff patch applier could write under .git (e.g. hooks) even though git apply would reject those paths. Patches are now rejected before any write when any path component matches git’s verify_path rules—case variants, trailing dots/spaces, git~1, NTFS stream suffixes, HFS ignorable Unicode, nested paths like sub/.git/config, and both --- and +++ sides on modify hunks. Look-alikes such as .gitignore still apply; a refused patch behaves like a non-applicable patch (warning + fixer runs).

Separately, create_state_dir_all creates directories under HK_STATE_DIR with 0700 on Unix (logs, output, stash patch backups), while parents outside that tree and already-existing dirs are unchanged. Call sites for patches, log files, and HK_OUTPUT_FILE now use this helper instead of plain create_dir_all.

New unit, bats, and state_dir_permissions tests cover .git spellings, end-to-end fix behavior, and state-dir mode.

Reviewed by Cursor Bugbot for commit f758bfd. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Security
    • Patches targeting Git’s internal .git directory are now rejected, while similarly named paths such as .gitignore remain valid.
  • Bug Fixes
    • Directories created within the application’s state directory now receive owner-only permissions on Unix. Parent directories retain their existing permissions.

…e state dir private

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 7 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2613306c-6024-447c-afc5-8e99bbed808a
📥 Commits

Reviewing files that changed from the base of the PR and between 0b55b95 and f758bfd.

📒 Files selected for processing (7)
  • src/env.rs
  • src/git.rs
  • src/logger.rs
  • src/main.rs
  • src/step/diff.rs
  • test/apply_check_diff.bats
  • test/state_dir_permissions.bats

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f8a634f3-5a0e-434f-a332-807f29b86c42
📥 Commits

Reviewing files that changed from the base of the PR and between 16bd59a and 0b55b95.

📒 Files selected for processing (7)
  • src/env.rs
  • src/git.rs
  • src/logger.rs
  • src/main.rs
  • src/step/diff.rs
  • test/apply_check_diff.bats
  • test/state_dir_permissions.bats

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change adds restricted-permission creation for state-directory paths and uses it for patches, logs, and output files. It also rejects patch operations that target Git’s internal directory and adds regression tests for both changes.

Changes

State directory permissions

Layer / File(s) Summary
Create and use restricted state directories
src/env.rs, src/git.rs, src/logger.rs, src/main.rs, test/state_dir_permissions.bats
A shared helper creates paths within the state directory with Unix mode 0700. The patches, log, and output paths use the helper. A Bats test checks state-directory and parent-directory permissions.

Git-internal patch path protection

Layer / File(s) Summary
Validate patch paths before application
src/step/diff.rs, test/apply_check_diff.bats
Patch application rejects paths that name Git’s internal directory, including recognized platform-specific spellings and either side of a modify operation. Tests cover rejected paths and confirm that .gitignore patches remain allowed.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 0b55b

The change makes hk's state directory private on Unix and refuses patches that write inside .git. No merge-blocking risk was identified from the supplied evidence.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 0b55b

The changes strengthen filesystem protections without expanding privileges. No introduced security regression was identified in the reviewed code. Existing directory permissions and crash-recovery limitations remain unchanged; cross-platform validation remains limited.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The relevant attack surface is patch text produced by a check_diff command and applied with hk’s filesystem authority. Sensitive outcomes include repository hook or configuration modification and exposure of saved changes or command output to other local users. The reviewed changes constrain those outcomes rather than add authority.

Security Findings and Attack Paths

  • inferred — The new preflight checks interrupt the described direct path from crafted patch headers to Git hook or configuration writes. This is a restriction on patch application, not a sandbox for the configured check or fixer commands.

Trust Boundaries and Controls

  • observed — Git-directory rejection supplements existing checks against paths outside the working directory, symlink traversal and multiple hard links. Owner-only state-directory creation adds a local confidentiality control for newly created Unix state directories.

Resilience and Maintainability Implications

  • observed — Returned write errors trigger best-effort restoration, and failed restoration stops execution instead of invoking the fixer on uncertain state. Direct writes and unlocked rollback are not a durable transaction across interruption or concurrent edits. Those recovery limitations predate this PR and are not retained as introduced concerns.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.42% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 19 functions across 7 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: refusing check_diff patches that write inside Git’s .git directory. It is concise and specific.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Critical risk] Blocks patches from writing to git internals.

This PR appears safe to merge; no outstanding finding or new actionable issue remains.

Summary

This PR rejects check_diff patches that name Git’s metadata directory and creates new state directories with owner-only permissions on Unix.

  • Patch rejection occurs before writes and falls back to the configured fixer.
  • The follow-up change restores support for a single-component relative HK_STATE_DIR.

Reviews (2) · Last reviewed commit: "fix(step): create a single-component rel..."

Comment thread src/env.rs Outdated
Comment thread src/step/diff.rs
@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Instruction counts

Nothing was compared, and so nothing was gated. No series appears on both sides: either the base has no measurements recorded, or the two were measured on different runner classes, which are deliberately not comparable — counts shift between machine types by more than a real regression does.

New, nothing to compare against: builtins on jdx-perf-v1-ubuntu24.04-x64-hk-rust1.97.1-img5263c143, check on jdx-perf-v1-ubuntu24.04-x64-hk-rust1.97.1-img5263c143, usage on jdx-perf-v1-ubuntu24.04-x64-hk-rust1.97.1-img5263c143, validate on jdx-perf-v1-ubuntu24.04-x64-hk-rust1.97.1-img5263c143

Only instruction counts gate. Wall clock is shown for context — on identical hardware it moves 4-20% run to run.

Measured by tak — instruction-counted CLI benchmarks, stored in this repository's git notes.

f758bfd51c20 vs a1a3cca13f9d · measured on the runner, not pushed to the history.

jdx and others added 2 commits October 3, 2026 19:44
@jdx
jdx enabled auto-merge (squash) October 3, 2026 21:26
@jdx
jdx merged commit 834668d into main Oct 3, 2026
31 of 33 checks passed
@jdx
jdx deleted the security/check-diff-reject-dot-git branch October 3, 2026 22:00
@jdx jdx mentioned this pull request Oct 3, 2026
jdx added a commit that referenced this pull request Oct 4, 2026
<!-- entire-trail-link-start -->
https://entire.io/gh/jdx/hk/trails/116
<!-- entire-trail-link-end -->

### 🚀 Features

- **(builtins)** keep CRLF files CRLF in trailing_whitespace, newlines,
and mixed_line_ending by [@jdx](https://github.com/jdx) in
[#1621](#1621)
- **(builtins)** report revive findings as diagnostics by
[@jdx](https://github.com/jdx) in
[#1626](#1626)
- **(builtins)** report buf_lint and clang_format findings as
diagnostics by [@jdx](https://github.com/jdx) in
[#1627](#1627)
- **(builtins)** report actionlint findings as diagnostics by
[@jdx](https://github.com/jdx) in
[#1623](#1623)
- **(builtins)** report flake8 findings as diagnostics by
[@jdx](https://github.com/jdx) in
[#1624](#1624)
- **(builtins)** report mypy, mado and six more linters as diagnostics
by [@jdx](https://github.com/jdx) in
[#1630](#1630)
- **(config)** read column-less, prefixed and summarized output with the
gcc diagnostic format by [@jdx](https://github.com/jdx) in
[#1629](#1629)
- **(init)** detect 47 more tools from their config files by
[@jdx](https://github.com/jdx) in
[#1576](#1576)
- **(mcp)** let agents run checks on changed or unstaged files only by
[@jdx](https://github.com/jdx) in
[#1600](#1600)
- **(mcp)** let agents run checks on staged files only by
[@jdx](https://github.com/jdx) in
[#1640](#1640)
- **(test)** assert normalized diagnostics in step tests by
[@jdx](https://github.com/jdx) in
[#1622](#1622)
- **(validate)** warn about misspelled properties, no-op depends,
unmatchable globs and loose min_hk_version by
[@jdx](https://github.com/jdx) in
[#1590](#1590)

### 🐛 Bug Fixes

- **(agent)** make --safe refuse a hook report with unknown or
destructive effect by [@jdx](https://github.com/jdx) in
[#1573](#1573)
- **(agent)** make Claude Code and Codex Stop hooks block with a valid
decision and exit 0 by [@jdx](https://github.com/jdx) in
[#1604](#1604)
- **(builtins)** check large files and case conflicts in binary files by
[@jdx](https://github.com/jdx) in
[#1571](#1571)
- **(check)** fail on unknown --step names instead of passing after
running no steps by [@jdx](https://github.com/jdx) in
[#1603](#1603)
- **(ci)** fail test:bats on a mistyped path or when no tests run by
[@jdx](https://github.com/jdx) in
[#1563](#1563)
- **(cli)** keep generated output exit codes on closed pipes by
[@jdx](https://github.com/jdx) in
[#1540](#1540)
- **(cli)** print failures once, without Rust locations or a bogus fix
hint by [@jdx](https://github.com/jdx) in
[#1588](#1588)
- **(config)** union user skip_steps, skip_hooks and hide_warnings with
the project's, and warn when hk.local.pkl replaces hk.pkl by
[@jdx](https://github.com/jdx) in
[#1596](#1596)
- **(config)** honor top-level jobs, accept documented HK_STASH values,
and fix the hk config crash by [@jdx](https://github.com/jdx) in
[#1594](#1594)
- **(config)** honor subproject skip_steps and warn about ignored
subproject settings by [@jdx](https://github.com/jdx) in
[#1615](#1615)
- **(docs)** stop sidebar links showing through the logo when it bounces
by [@jdx](https://github.com/jdx) in
[#1554](#1554)
- **(git)** skip tracked files with non-UTF-8 names with a warning in
--all, --glob, --from-ref and file arguments by
[@jdx](https://github.com/jdx) in
[#1613](#1613)
- **(init)** explain why a hook is missing and print next steps after hk
init by [@jdx](https://github.com/jdx) in
[#1564](#1564)
- **(install)** correct the --force-local message when a global install
exists by [@jdx](https://github.com/jdx) in
[#1616](#1616)
- **(install)** keep hook commands working after hk upgrades and under a
minimal PATH by [@jdx](https://github.com/jdx) in
[#1597](#1597)
- **(mcp)** make cancel_run stop the real hk run instead of sticking in
cancelling by [@jdx](https://github.com/jdx) in
[#1607](#1607)
- **(mcp)** cap step output in structured results and report pre-run
failures by [@jdx](https://github.com/jdx) in
[#1605](#1605)
- **(mcp)** include untracked and binary files in get_diff and surface
capture errors by [@jdx](https://github.com/jdx) in
[#1601](#1601)
- **(pkl)** load configs that amend the v1 package schema by
[@jdx](https://github.com/jdx) in
[#1560](#1560)
- **(pkl)** hide mirror credentials in failed package download errors by
[@jdx](https://github.com/jdx) in
[#1620](#1620)
- **(pkl)** show file, line and column for Pkl syntax errors, including
in imported files by [@jdx](https://github.com/jdx) in
[#1598](#1598)
- **(stash)** preserve CRLF when restoring unstaged files by
[@jdx](https://github.com/jdx) in
[#1535](#1535)
- **(stash)** restore unstaged text over a staged binary file no step
changed by [@jdx](https://github.com/jdx) in
[#1580](#1580)
- **(stash)** stash tracked files whose names start with a colon by
[@jdx](https://github.com/jdx) in
[#1614](#1614)
- **(stash)** keep a staged edit when its worktree copy was reverted to
HEAD by [@jdx](https://github.com/jdx) in
[#1602](#1602)
- **(stash)** keep a merge or cherry-pick in progress when stashing the
worktree by [@jdx](https://github.com/jdx) in
[#1609](#1609)
- **(stash)** restore hk's own stash entry by commit id so concurrent
stashes are never touched by [@jdx](https://github.com/jdx) in
[#1637](#1637)
- **(stash)** serialize stash and restore across hk processes and linked
worktrees by [@jdx](https://github.com/jdx) in
[#1636](#1636)
- **(stash)** restore stashed changes when hk is terminated and recover
them after a crash by [@jdx](https://github.com/jdx) in
[#1641](#1641)
- **(step)** isolate formatter patches from concurrent commands and
staging by [@nettlesh](https://github.com/nettlesh) in
[#1392](#1392)
- **(step)** keep interactive steps in hk's process group by
[@jdx](https://github.com/jdx) in
[#1557](#1557)
- **(step)** say "command not found" when an argv step's tool is not
installed by [@jdx](https://github.com/jdx) in
[#1579](#1579)
- **(step)** add exec_ok for condition shell tests and stop exec
panicking by [@jdx](https://github.com/jdx) in
[#1610](#1610)
- **(step)** run steps that depend on a step with no files instead of
hanging by [@jdx](https://github.com/jdx) in
[#1584](#1584)
- **(step)** select files in directories whose names contain brackets or
braces by [@jdx](https://github.com/jdx) in
[#1592](#1592)
- **(step)** keep percent signs in file names literal under cmd.exe by
[@jdx](https://github.com/jdx) in
[#1568](#1568)
- **(step)** end tools left running by a fail-fast cancel on Windows by
[@jdx](https://github.com/jdx) in
[#1625](#1625)
- **(step)** exclude a directory's files when a step or group exclude
names it by [@jdx](https://github.com/jdx) in
[#1606](#1606)
- **(step)** report diagnostic paths relative to the repository root for
steps that run in a subdirectory by [@jdx](https://github.com/jdx) in
[#1638](#1638)
- **(step)** run a custom shell directly on Windows and accept a quoted
shell path by [@jdx](https://github.com/jdx) in
[#1628](#1628)
- **(step)** batch Windows cmd shims below the cmd.exe line limit by
[@jdx](https://github.com/jdx) in
[#1618](#1618)
- **(step)** type scripts whose env shebang has -S, options or variables
by [@jdx](https://github.com/jdx) in
[#1611](#1611)
- **(step)** keep diagnostics from every started job when another job
fails by [@jdx](https://github.com/jdx) in
[#1642](#1642)
- **(step)** report steps stopped by Ctrl-C as cancelled instead of
failed by [@jdx](https://github.com/jdx) in
[#1643](#1643)
- **(util)** check files whose 8 KiB probe splits a multibyte char and
keys in non-UTF-8 files by [@jdx](https://github.com/jdx) in
[#1572](#1572)
- **(util)** accept git-generated merge and revert titles in
check-conventional-commit by [@jdx](https://github.com/jdx) in
[#1566](#1566)
- **(util)** check protected branch, exec bits and case conflicts
against git's own state by [@jdx](https://github.com/jdx) in
[#1577](#1577)
- **(util)** keep non-UTF-8 files and symlinks intact in
fix-smart-quotes by [@jdx](https://github.com/jdx) in
[#1570](#1570)
- **(validate)** reject dependency cycles, self-dependencies and invalid
step globs at load time by [@jdx](https://github.com/jdx) in
[#1567](#1567)

### 📚 Documentation

- **(builtins)** show file selectors, script commands and options in the
catalogue by [@jdx](https://github.com/jdx) in
[#1562](#1562)
- **(config)** explain where diagnostics come from and when a step needs
diagnostic_format by [@jdx](https://github.com/jdx) in
[#1619](#1619)
- **(config)** document sharing one configuration across repositories by
[@jdx](https://github.com/jdx) in
[#1591](#1591)
- **(settings)** describe pkl rewrite and CA variables for the built-in
evaluator by [@jdx](https://github.com/jdx) in
[#1617](#1617)
- refresh competitor benchmark for v2.4.0 by
[@jdx](https://github.com/jdx) in
[#1536](#1536)
- replace the sea shanty with Bound for the Main and its music video by
[@jdx](https://github.com/jdx) in
[#1541](#1541)
- take the new shanty video down if its poster cannot be published by
[@jdx](https://github.com/jdx) in
[#1543](#1543)
- add a sea shanty mode to the landing page by
[@jdx](https://github.com/jdx) in
[#1544](#1544)
- keep a link to the sea shanty when scripting is off by
[@jdx](https://github.com/jdx) in
[#1545](#1545)
- generate the pirate CLI reference from the English one by
[@jdx](https://github.com/jdx) in
[#1550](#1550)
- restrict AI replies on Discussions and Issues and note the instant ban
by [@jdx](https://github.com/jdx) in
[#1559](#1559)
- start getting started with a first configuration that needs no linters
by [@jdx](https://github.com/jdx) in
[#1561](#1561)
- correct stash, type-checker lock, and patch-apply descriptions by
[@jdx](https://github.com/jdx) in
[#1578](#1578)
- add lefthook and husky with lint-staged migration guides by
[@jdx](https://github.com/jdx) in
[#1589](#1589)

### ⚡ Performance

- **(config)** evaluate only the builtins a config uses, cutting cold
load from ~1.4 s to ~0.07 s by [@jdx](https://github.com/jdx) in
[#1581](#1581)
- **(git)** skip the untracked scan for runs that name their files, e.g.
hk check FILE by [@jdx](https://github.com/jdx) in
[#1587](#1587)
- **(stash)** restore unstaged files no step changed with one git
process by [@jdx](https://github.com/jdx) in
[#1612](#1612)
- **(stash)** stop rescanning the worktree before stashing, saving ~80
ms on large repos by [@jdx](https://github.com/jdx) in
[#1595](#1595)
- **(step)** resolve workspace_indicator once per directory, saving ~120
ms per step in large monorepos by [@jdx](https://github.com/jdx) in
[#1599](#1599)
- **(step)** run read-only check-only steps side by side in fix hooks by
[@jdx](https://github.com/jdx) in
[#1586](#1586)
- use mimalloc as the global allocator by [@jdx](https://github.com/jdx)
in [#1635](#1635)
- bump pklr to 4.0.0 (hk validate 14x faster) by
[@jdx](https://github.com/jdx) in
[#1644](#1644)

### 🧪 Testing

- **(windows)** run every e2e-win case and cover fix, staging and
installed hooks by [@jdx](https://github.com/jdx) in
[#1574](#1574)
- fix bats teardown and event-order flakes and surface CI retries by
[@jdx](https://github.com/jdx) in
[#1583](#1583)

### 🛡️ Security

- **(deps)** update dependency esbuild to ^0.28.0 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#1548](#1548)
- **(step)** refuse check_diff patches that write inside .git by
[@jdx](https://github.com/jdx) in
[#1585](#1585)
- point the landing page at the sea shanty by
[@jdx](https://github.com/jdx) in
[#1542](#1542)
- turn sea shanty mode into a pirate edition of the whole docs site by
[@jdx](https://github.com/jdx) in
[#1549](#1549)
- document what hk executes, global hook reach, and --safe limits in a
security model page by [@jdx](https://github.com/jdx) in
[#1575](#1575)
- bring the sea shanty contributing page up to date with the English
page by [@jdx](https://github.com/jdx) in
[#1631](#1631)

### 🔍 Other Changes

- **(benchmark)** gate hook stashing and cold config loads in the perf
check by [@jdx](https://github.com/jdx) in
[#1608](#1608)
- **(entire)** store checkpoints in a private repository by
[@jdx](https://github.com/jdx) in
[74bd981](74bd981)
- **(entire)** commit codex session hooks by
[@jdx](https://github.com/jdx) in
[71966d2](71966d2)
- **(entire)** commit claude session hooks by
[@jdx](https://github.com/jdx) in
[2298ebf](2298ebf)
- **(entire)** store checkpoints in a private repository by
[@jdx](https://github.com/jdx) in
[4a4e806](4a4e806)
- **(entire)** restore lower-cost trail findings by
[@jdx](https://github.com/jdx) in
[6eed674](6eed674)
- **(install)** refuse legacy hook installs that overwrite foreign hooks
or symlinks by [@jdx](https://github.com/jdx) in
[#1593](#1593)
- **(release)** smoke-run every release archive before the release is
created by [@jdx](https://github.com/jdx) in
[#1565](#1565)
- **(security)** check RustSec advisories in CI and document gh release
verify by [@jdx](https://github.com/jdx) in
[#1569](#1569)
- unblock the docs deploy and stop re-rendering unchanged videos by
[@jdx](https://github.com/jdx) in
[#1551](#1551)
- limit each contributor to one open draft PR by
[@jdx](https://github.com/jdx) in
[#1555](#1555)
- lint test code by running clippy with --all-targets by
[@jdx](https://github.com/jdx) in
[#1582](#1582)
- combine hosted Bats configurations by [@jdx](https://github.com/jdx)
in [#1633](#1633)
- skip full checks for docs-only pull requests by
[@jdx](https://github.com/jdx) in
[#1634](#1634)
- remove redundant package.homepage from Cargo.toml by
[@jdx](https://github.com/jdx) in
[#1639](#1639)

### 📦️ Dependency Updates

- lock file maintenance by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#1537](#1537)
- update jdx crates by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#1539](#1539)
- update github actions by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#1546](#1546)
- update dependency aube to latest by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#1547](#1547)
- update dependency communique to latest by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#1553](#1553)
- update rust crate clx to v3.0.3 by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#1552](#1552)
- update dependency aube to latest by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#1556](#1556)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Mechanical version and documentation URL updates plus changelog; no
runtime code changes in the diff.
> 
> **Overview**
> **Release cut** that bumps the crate and CLI from **2.4.0** to
**2.5.0** (`Cargo.toml`, `Cargo.lock`, `hk.usage.kdl`, generated CLI
metadata) and adds the **[2.5.0] CHANGELOG** section dated 2026-10-04,
cataloguing everything shipped since v2.4.0.
> 
> Doc and sample configs are updated so `amends` / `import` examples
point at `package://…/v2.5.0/hk@2.5.0` (README, getting started,
migration guides, pirate docs, public `.pkl` examples, `hk-example.pkl`,
and a commented line in `hk.pkl`). No application logic changes appear
in this diff—only versioning and release notes alignment.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
8d358e2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: mise-en-dev <123107610+mise-en-dev@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant