feat(init): add --native-hook for cross-platform hook support (Windows) - #1090
feat(init): add --native-hook for cross-platform hook support (Windows)#1090ayoub-khemissi wants to merge 2 commits into
Conversation
Adds `rtk init -g --native-hook` which installs `rtk hook copilot` as
the Claude Code PreToolUse hook instead of the bash script. This enables
hook-based token savings on Windows (where bash is unavailable) and
removes the jq dependency on all platforms.
- Add NATIVE_HOOK_COMMAND constant ("rtk hook copilot")
- Add --native-hook flag to Init command (opt-in on Unix, required on Windows)
- Add run_native_hook_mode() and run_native_hook_only_mode() install paths
- Fix remove_hook_from_json() to also remove native hook entries
- Fix hook_already_present() for symmetric variant-specific detection
- Add replace_other_rtk_hook_in_json() for bash↔native migration
- Fix show_claude_config() to detect native hook in settings.json
- Add --codex + --native-hook conflict validation
- Add 15 tests covering all edge cases
|
Hi @ayoub-khemissi — nice work on this, the native binary approach is clearly the right long-term answer for Windows users without git-bash. Opened #1123 earlier today taking an orthogonal angle on the same Windows gap: instead of adding a native path, it unblocks the existing bash hook by removing the The two PRs look non-conflicting at the code level:
If both merge, Windows users get:
With only this PR, Your PR has much broader scope (500 LOC, 15 tests, 5 edge-case fixes, migration logic) and is clearly the primary Windows effort here — #1123 is a small complement, not a replacement. Happy to close #1123 if you'd prefer a single-PR path, or keep it open if the maintainers see value in shipping both. Independent verification data, if useful for your review: AccelixGames team ran the bash hook path end-to-end on Windows 11 + Claude Code CLI + git-bash via hand-install ( Closes the issues from the opposite direction than yours, but we both end up helping Windows users either way. 👍 |
Follow-up:
|
|
I'm having the same problem here, looking forward to this update. |
|
Really need this! |
|
Can't wait for this to be merged! For anyone eager to try it early, build from source with: |
|
Hey, solved on latest releases 0.37.x Thanks for contributing and trying to address this issue. Closing this for clean up |
Context: Why a New PR Instead of Rebasing #150
PR #150 (
feat/native-hook-rewrite) added native cross-platform hooks via 8 per-language Rust modules (src/hook/git.rs,cargo.rs,files.rs, etc. -- 1905 lines across 14 files).Since then, upstream
develophas evolved significantly:rtk rewrite— single source of truth for LLM hook rewrites #241 (rtk rewrite) introduced a registry-based rewrite system (src/discover/registry.rs+rules.rs) that replaced all per-language rewrite logic with centralized regex rulessrc/to nested subfolders (cmds/,core/,hooks/,analytics/)hooks/hook_cmd.rsnow has native Rust hook processing (run_copilot(),run_gemini()) that reads JSON from stdin and usesdiscover::registry::rewrite_command()--json, git-C, cat multi-file, grep flag reorder) were independently fixed in the registryRebasing was not viable: 9 commits over a massively reorganized repo produced modify/delete conflicts on the first commit. The 8 per-language modules were 100% superseded by the registry.
This PR takes the clean approach: fresh branch from
develop, reusing the existingrtk hook copilotcommand (already in upstream), adding only what was missing -- the--native-hookflag inrtk initto install it as the hook on all platforms.Result: 3 files, 500 LOC (vs 14 files, 1905 LOC in #150).
Note: CLA has been signed as requested in the #150 review.
What This PR Does
Adds
rtk init -g --native-hookwhich installsrtk hook copilotas thePreToolUsehook command instead of the bash scriptrtk-rewrite.sh.Why
bash+jq-- unavailable on Windows. Currently,rtk init -gfalls back to--claude-mdmode on Windows (no automatic rewriting). With--native-hook, Windows users get the same hook-based token savings as Unix users.rtkbinary itself. Nobash, nojq, no shell script on disk.--native-hookis available for those who prefer the native path.Behavior Matrix
rtk init -g--claude-md+ message suggesting--native-hookrtk init -g --native-hookrtk hook copilotrtk hook copilotrtk init -g --hook-only--native-hookrtk init -g --hook-only --native-hookrtk init --showrtk init -g --uninstallrtk init --codex --native-hookCode Changes (3 files, 500 insertions, 19 deletions)
src/hooks/constants.rs(+3 lines)Added
NATIVE_HOOK_COMMANDconstant:src/main.rs(+7 lines)Added
--native-hookflag to theInitcommand enum and passed it through tohooks::init::run().src/hooks/init.rs(+490 lines, -19 lines)New functions
is_rtk_hook_command(cmd) -> boolrtk-rewrite.shOR nativertk hook copilot)replace_other_rtk_hook_in_json(root, cmd) -> boolsettings.jsonto prevent both hooks runningrun_native_hook_mode(global, patch_mode, verbose, opencode) -> Result<()>"rtk hook copilot"run_native_hook_only_mode(global, patch_mode, verbose) -> Result<()>Bug fixes (5 edge cases found and fixed)
remove_hook_from_json()only checked forrtk-rewrite.shrtk init -g --uninstallwould NOT remove the native hook from settings.jsonis_rtk_hook_command()-- now removes both variantshook_already_present()had unconditionalcmd == NATIVE_HOOK_COMMANDreplace_other_rtk_hook_in_json()call inpatch_settings_json()before inserting new entryshow_claude_config()only checked bash hook file on diskrtk init --showshowed[--] Hook: not foundwhen native hook was installed (no file on disk)NATIVE_HOOK_COMMANDwhen bash file does not exist--codex --native-hooksilently ignored--native-hook--codex cannot be combined with --native-hookModified existing functions
run()native_hook: boolparameter, passes torun_default_modeandrun_hook_only_moderun_default_mode(Unix)run_native_hook_mode()whennative_hookis truerun_default_mode(Windows)--native-hookrun_hook_only_mode(Unix)run_native_hook_only_mode()whennative_hookis truerun_hook_only_mode(Windows)hook_already_present()remove_hook_from_json()is_rtk_hook_command()instead ofcontains(REWRITE_HOOK_FILE)(see fix #1)patch_settings_json()replace_other_rtk_hook_in_json()before insert (see fix #3)show_claude_config()Tests Added (15 new, 1376 total)
All tests are unit tests in
src/hooks/init.rs#[cfg(test)] mod tests.is_rtk_hook_command(1 test, 8 assertions)test_is_rtk_hook_command"rtk hook copilot"true, various bash paths true,"rtk hook gemini"false,""false,"rtk hook copilot"(extra spaces) false,"/some/other/hook.sh"falsehook_already_present(6 tests)test_hook_already_present_native_hooktruetest_hook_already_present_native_when_bash_existsfalse(allows migration)test_hook_already_present_bash_when_native_existsfalse(allows migration)test_hook_already_present_both_hookstruefor either querytest_hook_already_present_empty_command""falsetest_hook_already_present_similar_but_different"rtk hook gemini"in JSONfalsefor both native and bash queriesremove_hook_from_json(3 tests)test_remove_native_hook_from_jsontest_remove_native_hook_preserves_other_hookstest_remove_both_hooks_from_jsonreplace_other_rtk_hook_in_json(4 tests)test_replace_other_rtk_hook_bash_to_nativetest_replace_other_rtk_hook_native_to_bashtest_replace_other_rtk_hook_no_replacement_when_same_varianttest_replace_other_rtk_hook_non_rtk_commandValidation (1 test)
test_codex_mode_rejects_native_hookrun(..., codex=true, native_hook=true, ...)"--codex cannot be combined with --native-hook"Quality Gates
cargo fmt --all --checkcargo clippy --all-targetscargo test --allManual Testing (Windows 11)
rtk init -g --native-hook --auto-patchrtk init --show[ok] Hook: rtk hook copilot (native, cross-platform)git status(via AI agent)rtk git status, compact outputrtk init -g --auto-patch(no --native-hook)rtk init -g --auto-patchthenrtk init -g --native-hook --auto-patchrtk init --codex --native-hookrtk init -g --uninstallrtk init --show[--] Hook: not found, clean stateSupersedes
Supersedes #150 -- all unique functionality ported. The per-language hook modules from #150 are no longer needed since the registry-based rewrite system in
develophandles all command rewriting.