refactor: replace external hook scripts with native 'rtk hook claude' command#785
refactor: replace external hook scripts with native 'rtk hook claude' command#785romansl wants to merge 10 commits intortk-ai:developfrom
Conversation
📊 Automated PR Analysis
SummaryReplaces the external bash hook script (hooks/rtk-rewrite.sh) with a native 'rtk hook claude' command built into the Rust binary. This eliminates dependencies on bash and jq, removes file-based hook installation/version checking, and simplifies the setup to just 'rtk init -g' across all platforms. Review Checklist
Analyzed automatically by wshm · This is an automated analysis, not a human review. |
pszymkowiak
left a comment
There was a problem hiding this comment.
Thanks @romansl — this is the right long-term direction. Eliminating bash + jq saves ~5-10ms per hook call, which adds up over a full session.
However, the PR has 3 blocking issues and a scope concern:
P0 — Security regression
-
Permission deny/ask rules dropped —
run_claude()callsrewrite_command()but nevercheck_command()fromsrc/permissions.rs. Every command that should be denied or prompted is silently auto-allowed. This is a security regression shipped in PR #576. -
Audit logging dropped —
RTK_HOOK_AUDIT=1writes to~/.local/share/rtk/hook-audit.logare gone. Users relying onrtk hook-auditget an empty log after upgrading. -
Migration breaks existing installs —
hook_already_presentno longer matchesrtk-rewrite.shin settings.json. Runningrtk init -gafter upgrade inserts a duplicate hook entry instead of replacing the old one.
P1 — Missing wiring
rtk hook claudesubcommand doesn't exist inHookCommandsenum — clap returns "unrecognized subcommand"hook_check::status()always returns OK —rtk gainloses the "run rtk init" diagnostic
Scope concern — should cover all agents, not just Claude Code
RTK supports 10 AI tools (Claude, Gemini, Copilot, Cursor, Windsurf, Cline, Codex, OpenCode, OpenClaw, Vibe). This PR only makes the hook native for Claude Code. If we go native for one, we should plan the same for Gemini (rtk hook gemini) and Copilot (rtk hook copilot) to avoid maintaining two architectures (bash hooks + native hooks) indefinitely.
We'd prefer a unified design:
rtk hook claude(this PR, once fixed)rtk hook gemini(already exists, needs same native treatment)rtk hook copilot(already exists, needs same native treatment)- All sharing the same permission check + audit log + 4-exit-code protocol
Next steps: we need to think about how to architect this properly across all agents. Are there other PRs or designs you're aware of that tackle this? We'd like to avoid fragmented efforts. Let us know if you'd like to collaborate on a broader design before reworking — we can open a design issue to align.
We'll assign @aeppling for architectural review as well. Thanks for the solid foundation!
The shell-based hook (rtk-rewrite.sh) requires jq to parse JSON from Claude Code / Cursor. When jq is not installed the hook exits silently (exit 0, no output), causing every command to fall through without RTK rewriting — resulting in zero token savings with no visible error. Add a soft check during `rtk init -g` that warns the user if jq is not found on PATH, with install instructions for common platforms. This is a stopgap until the hook is replaced by a native Rust subcommand (see rtk-ai#785). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The shell-based hook (rtk-rewrite.sh) requires jq to parse JSON from Claude Code / Cursor. When jq is not installed the hook exits silently (exit 0, no output), causing every command to fall through without RTK rewriting — resulting in zero token savings with no visible error. Add a soft check during `rtk init -g` that warns the user if jq is not found on PATH, with install instructions for common platforms. This is a stopgap until the hook is replaced by a native Rust subcommand (see rtk-ai#785). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Hey We are cleaning up the codebase and improving the project structure for better onboarding. As part of this effort, PR #826 reorganizes No logic changes — only file moves and import path updates. What you need to doRebase your branch on git fetch origin && git rebase origin/developGit detects renames automatically. If you get import conflicts, update the paths: use crate::git; // now: use crate::cmds::git::git;
use crate::tracking; // now: use crate::core::tracking;
use crate::config; // now: use crate::core::config;
use crate::init; // now: use crate::hooks::init;
use crate::gain; // now: use crate::analytics::gain;Need help rebasing? Tag @aeppling |
The shell-based hook (rtk-rewrite.sh) requires jq to parse JSON from Claude Code / Cursor. When jq is not installed the hook exits silently (exit 0, no output), causing every command to fall through without RTK rewriting — resulting in zero token savings with no visible error. Add a soft check during `rtk init -g` that warns the user if jq is not found on PATH, with install instructions for common platforms. This is a stopgap until the hook is replaced by a native Rust subcommand (see rtk-ai#785). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… command
BREAKING CHANGE: External hook scripts (hooks/rtk-rewrite.{sh,py}) are no
longer installed or used. Existing hook files can be safely deleted.
- Add native 'rtk hook claude' command that reads/writes JSON directly
- Remove hooks/rtk-rewrite.sh embedded script (no longer needed)
- Update init.rs: install hook via 'rtk hook claude' instead of script files
- Update hook_check.rs: native hook is always available (no file checks)
- Remove dead code: prepare_hook_paths(), ensure_hook_installed()
- Add cross-platform support (works identically on Windows, macOS, Linux)
Benefits:
- No external dependencies (Python, bash, jq)
- Faster: no fork/exec subprocess overhead
- Simpler installation: just 'rtk init -g', no script files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove external hook script checks (.sh/.py) from show_claude_config - Remove integrity checks for deprecated hook scripts - Check native hook via settings.json with "rtk hook claude" command - Update hook_check test to reflect native hook behavior (always Ok) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove functions and tests that were checking for legacy .sh/.py hook files, which are no longer used after switching to native 'rtk hook claude' command. - Remove hook_installed_path() (checked for .sh/.py files) - Remove parse_hook_version() (parsed version from hook scripts) - Remove CURRENT_HOOK_VERSION constant - Remove 6 tests for parse_hook_version() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Python hooks were only in local development branch, not in upstream. Remove .py extension logic, keep only .sh for legacy cleanup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After rebase, the hook_cmd::run_claude() call was missing the hooks:: prefix that other hook_cmd calls have, causing compilation failure. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The run_claude(), run_copilot(), and run_gemini() hook entry points were calling rewrite_command() directly without first calling check_command(), allowing all commands to bypass deny/ask rules. This commit adds permission checking to all three hook functions: - handle_vscode(): Returns early on Deny (no output), prompts on Ask - handle_copilot_cli(): Blocks Deny rules before showing suggestions - run_gemini(): Added print_deny() and print_ask() helpers Also added 9 new unit tests to verify permission checking behavior. Security issue: Commands that should be denied or require user confirmation were silently auto-allowed by the hook system. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
During the refactor from external bash hook scripts to native 'rtk hook claude' command, the audit logging functionality was not migrated to the Rust code. Users relying on 'rtk hook-audit' got an empty log after upgrading. This commit restores the audit_log() function and adds calls at all hook entry points (run_copilot, run_claude, run_gemini, handle_vscode, handle_copilot_cli). Actions logged: - rewrite: command was rewritten to rtk equivalent - skip:no_match: no rtk equivalent found - skip:deny_rule: deny rule matched - skip:heredoc: heredoc detected - skip:empty: empty input - ask: ask rule matched Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes migration issue where running `rtk init -g` after upgrade would create duplicate hook entries instead of replacing the old one. Changes: - Add legacy_hook_present() to detect old rtk-rewrite.sh hooks - Auto-migrate old hooks to new "rtk hook claude" command - Add 7 tests covering migration scenarios Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously always returned Ok, making the "run rtk init" diagnostic in `rtk gain` dead code. Now inspects ~/.claude/settings.json for: - native "rtk hook claude" hook → Ok - legacy rtk-rewrite.sh script → Outdated - no hook configured → Missing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
a124186 to
55a5d5b
Compare
|
I asked Claude to fix all the comments (each fix in a separate commit).
Unfortunately, I was only able to do this for Claude Code and I don't know how this will work for other agents. |
The integrity module (integrity.rs) provided SHA-256 verification for
external hook scripts (~/.claude/hooks/rtk-rewrite.{sh,py}). With the
migration to native hooks (rtk hook claude command in settings.json),
this verification is no longer applicable.
Changes:
- Remove src/hooks/integrity.rs module entirely
- Move compute_hash() to trust.rs (still needed for TOML filter trust)
- Remove runtime_check() call from main.rs startup
- Remove is_operational_command() function (now unused)
- Remove remove_hash() call from init.rs uninstall flow
- Update all documentation to reference native hooks instead of scripts
Native hooks are defined directly in settings.json and don't require
separate script files, making the integrity check obsolete.
Cursor and Gemini agents still use external scripts but were never
covered by this integrity system (they live in different directories).
|
PR #862 (by @FlorianBruniaux) implements the same goal — replacing external hook scripts with a native rtk hook claude command, and is actively maintained, MERGEABLE, and further along in review. Thank you @romansl for the significant effort, the architectural direction you proposed is essentially what #862 is delivering. |
BREAKING CHANGE: External hook scripts (hooks/rtk-rewrite.sh) are no longer installed or used. Existing hook files can be safely deleted.
Benefits:
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com