feat(hooks): add cross-platform Node.js/Bun hook for Windows support#141
feat(hooks): add cross-platform Node.js/Bun hook for Windows support#141schizoidcock wants to merge 2 commits intortk-ai:masterfrom
Conversation
## What
- Add `rtk-rewrite.js` - a cross-platform version of the hook that works on Windows, macOS, and Linux
- Update `rtk-awareness.md` with installation instructions for both platforms
## Why
The original `rtk-rewrite.sh` bash script doesn't work on Windows because:
1. Windows can't execute `.sh` files directly
2. Even with Git Bash, the path resolution and stdin handling differs
## Key Features
1. **Cross-platform**: Works with Node.js or Bun on any OS
2. **Chained command support**: Handles `cd "..." && git status` patterns
- Claude Code often generates commands like `cd "/path" && git status`
- Extracts the last command after `&&` for matching
- Rewrites to: `cd "/path" && rtk git status`
3. **No external dependencies**: Only requires Node.js or Bun (no jq needed on Windows)
4. **Same pattern matching**: Equivalent regex logic to the bash version (grep/sed)
## Platform Recommendations
- **macOS/Linux**: Use `rtk-rewrite.sh` (bash + grep/sed + jq) - battle-tested
- **Windows**: Use `rtk-rewrite.js` (Node.js/Bun) - native compatibility
## Usage (Windows)
```json
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "bun C:/Users/YOU/.claude/hooks/rtk-rewrite.js" }]
}]
}
}
```
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Thanks for tackling Windows support — that's a real gap today. A few points:
The cross-platform goal is valid, but let's make sure we don't regress the zero-dependency Unix experience. |
|
Thanks for the detailed feedback, @FlorianBruniaux! 1. Re: Overlap with #131 (chained command rewriting)I've reviewed #131 in detail. The key architectural difference:
After #131 merges, the bash hook becomes a forwarding shim that requires the rtk binary. Users who want hook functionality must have the binary installed. This PR provides: native Windows support for RTK hooks. Windows users can now use RTK natively — no path configuration issues, no WSL required, no compatibility problems. Secondary benefits:
Both approaches can coexist:
The chained command logic isn't duplicated effort — it's the same feature implemented for different deployment models. 2. Re: Node.js/Bun dependency concernThe bash hook remains the default on Unix/macOS/Linux. Nothing changes for existing users. The JS hook is an opt-in alternative that:
Install story comparison:
For most developers, Node.js/Bun is already installed — making this a lighter path than downloading a Rust binary or setting up WSL. 3. Re: CI statusCI is not failing — it's waiting for maintainer approval. The workflows triggered but show
This is standard GitHub Actions behavior for PRs from forks (security measure). A maintainer needs to approve the workflow runs — nothing broken on our side. The changes only touch:
No Rust code, Cargo.toml, or core functionality affected. SummaryThis PR and #131 serve different user segments:
Proposal: Merge both. Document JS hook as "lightweight alternative" in README. Let users choose based on their needs. |
|
Hey @schizoidcock, thanks for this pr and your assessment! The JS hook is well thought out, and you clearly dug into the details that matter (env var prefix handling, heredoc bypass, preserving all tool_input fields on rewrite). There's significant overlap with PR #156 - Hook Engine + Chained Command Rewriting, which was split out from #131 per @FlorianBruniaux's feedback. It takes a different approach: the hook logic lives inside the rtk binary itself ( I went through your diff and compared it with PR #156, and the feature coverage lines up nicely. PR #156 also uses a lexer instead of regex for chained commands (so I think having two separate hook implementations to keep in sync would be tricky over time, especially if a major dependency like js were to be added, so it's probably worth picking one path. Since the compiled binary approach doesn't introduce a Node.js or Bun dependency (which was @FlorianBruniaux's concern as well), I think it's a good fit. That said, I'd really like to hear if you see gaps or scenarios where the binary approach falls short, for instance, some additional changes might be needed if additional shell languages need to be supported. |
Replace regex-based chain parsing with proper lexer that: - Handles &&, ||, ; operators (not just &&) - Is quote-aware: "Fix && cleanup" won't incorrectly split - Detects shellisms (globs, pipes, redirects, subshells) - Rewrites each command in chain independently Add 49 tests covering: - Quote awareness edge cases - Chain parsing with mixed operators - Shellism detection and passthrough - All supported command rewrites - Env var prefix handling Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Thanks for the detailed feedback @ahundt! I've updated the JS hook with a proper quote-aware lexer that now:
Re: "Node.js as dependency" concern: The target users of RTK hooks are AI coding tool users. Let's look at what they already have installed:
Node.js/Bun is effectively already a dependency for 95%+ of RTK's target audience. It's not adding a new dependency — it's leveraging one that's already there. Key advantages of the JS hook approach:
The JS hook works as a proxy — it reads stdin, transforms, writes stdout. No process spawn overhead, no binary to download/update, no platform-specific builds. Adding support for a new tool is trivial: if (matchBinary === 'bun' && matchArgs[1] === 'test') {
return `${envPrefix}rtk bun test ${argsToString(matchArgs.slice(2))}`.trim();
}Proposal: Both approaches can coexist:
Happy to discuss or close if the team prefers a single path. |
|
thank for all the work here ! I think it's better for security and speed to go full binary "rtk hook". With a compiled Rust binary, we can implement binary signature verification — RTK would verify the hook's cryptographic signature (e.g. embedded ed25519 or codesigning) before execution, ensuring the hook hasn't been tampered with. This is something we simply can't enforce with a .js script that anyone can modify. This gives us a tamper-proof trust chain that's impossible with an interpreted script approach. Happy to discuss implementation details. we already receive a security PR in this way. |
|
Thanks @pszymkowiak for the security perspective! A few counterpoints to consider: 1. Threat model: The hook runs locally If an attacker can modify
Binary signature verification protects against distribution tampering, not local compromise. 2. Auditability cuts both ways
A signed binary means "trust RTK's build pipeline." A readable JS file means "trust what you can see." For security-conscious users, auditability > signatures. 3. Supply chain considerations Binary signature verification requires:
With JS, users can fork, audit, and run their own version. The attack surface is actually smaller because there's no trusted third party. 4. The real security risk The hook receives commands from the AI and rewrites them. The security-critical question is: "Does the rewrite logic do what it claims?"
That said, I understand the preference for a single implementation path. If the team decides to go binary-only, I'm happy to close this PR. Just wanted to present the counterarguments for consideration. |
|
Thanks for the thorough discussion and the quality of this PR — the lexer, the test suite, and the edge case handling are genuinely solid work. You make a fair point about auditability vs signatures. For the local threat model you describe, you're right that a signed binary doesn't protect against someone who already has That said, we're going with the binary approach (rtk hook claude from PR #156) as the single path forward. The main reasons:
Regarding your point about contribution barrier and extensibility — we agree that rewrite rules shouldn't require Rust knowledge to modify. The plan is to externalize the rule definitions If you'd like to explore that direction, we'd love a PR that designs the TOML schema for custom rewrite rules — something like: [[rewrite]] That would let anyone add patterns for their stack without touching Rust. Your understanding of the rewrite logic from this PR puts you in a great position to design that. Happy to please close in favor of #156, but genuinely appreciate the effort and hope to see you on another PR! |
|
Thanks for the thorough discussion and the quality of this PR — the lexer, the test suite, and the edge case handling are genuinely solid work. You make a fair point about auditability vs signatures. For the local threat model you describe, you're right that a signed binary doesn't protect against someone who already has filesystem access. That said, we're going with the binary approach (
Regarding your point about contribution barrier and extensibility — we agree that rewrite rules shouldn't require Rust knowledge to modify. The plan is to externalize the rule definitions into TOML configuration, so users and contributors can add/customize rewrite patterns without touching Rust code or recompiling. The binary handles the lexer, chain parsing, and execution; TOML handles the "what to rewrite" part. If you'd like to explore that direction, we'd love a PR that designs the TOML schema for custom rewrite rules — something like: [[rewrite]]
match = "terraform"
subcommands = ["plan", "apply", "init"]
rewrite = "rtk {command}"That would let anyone add patterns for their stack without touching Rust. Your understanding of the rewrite logic from this PR puts you in a great position to design that. Happy to discuss in GitHub Discussions if you prefer. Closing in favor of #156, but genuinely appreciate the effort and hope to see you on another PR! |
What
rtk-rewrite.js- a cross-platform version of the hook that works on Windows, macOS, and Linuxrtk-awareness.mdwith installation instructions for both platformsWhy
The original
rtk-rewrite.shbash script doesn't work on Windows because:.shfiles directlyKey Features
cd "..." && git statuspatternscd "/path" && git status&&for matchingcd "/path" && rtk git statusPlatform Recommendations
rtk-rewrite.sh(bash + grep/sed + jq) - battle-testedrtk-rewrite.js(Node.js/Bun) - native compatibilityUsage (Windows)
{ "hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "bun C:/Users/YOU/.claude/hooks/rtk-rewrite.js" }] }] } }