Conversation
Rebased onto current develop (binary-command era). Threads dry_run through patch_settings_json_command, migrate_old_hook_script, install_cursor_hooks, run_kilocode_mode, run_antigravity_mode, run_gemini, run_copilot, and uninstall. Fixes from PR #1032 review: - --uninstall --dry-run no longer deletes files (uninstall() now takes dry_run, every fs::remove_file / fs::write / atomic_write guarded) - Success messages ("installed", "configured", "Restart ...") gated on !dry_run in run_default_mode, run_hook_only_mode, run_codex_mode, run_copilot, install_cursor_hooks, run_gemini - prompt_telemetry_consent() skipped in dry-run - integrity::store_hash() in run_gemini guarded - KiloCode and Antigravity modes now accept dry_run - PatchResult::WouldPatch variant added for patch_settings_json_command - [dry-run] Nothing written. footer printed by every sub-mode - write_if_changed uses atomic_write (not fs::write) Added integrity::hash_path_for() public wrapper so dry-run can check sidecar existence without the destructive remove_hash. Tests: write_if_changed(dry_run=true) creates nothing; run_codex_mode_with_paths(dry_run=true) creates neither RTK.md nor AGENTS.md. 1596 tests pass, clippy clean.
…removals
The two remove_file calls flagged by semgrep already existed pre-PR.
Wrapping them in if dry_run { print } else { remove_file } shifted
their context enough that --baseline-commit re-attributed them as new.
The rule's own message says deletion is expected in hooks/init cleanup.
Addresses three review items from PR #1032: - Bundle verbose+dry_run into a Clone+Copy InitContext struct (mirrors RunOptions in src/core/runner.rs). Collapses 25+ function signatures that already carried both fields and makes future flags one struct field instead of N signature changes. - Emit "[dry-run] Nothing written." exactly once from the top-level run() and uninstall() exit points instead of from every sub-mode. Fixes the double footer when --agent cursor combined with default mode. - Reject --show with --dry-run via clap conflicts_with rather than silently ignoring --dry-run. - Add regression tests for run_default_mode and uninstall dry-run paths using the existing with_claude_dir_override scaffolding.
Adds a "Preview without writing" subsection under Step 1 covering the --dry-run flag, -v interaction for content preview, that telemetry consent is skipped, and the --show conflict. Required by CONTRIBUTING.md section 4 (new features need documentation).
Adds rtk gradlew command for build, test, lint, and dependency operations on Gradle projects. Filters task progress noise, preserves build scan URLs, test failures, lint violations, and compiler warnings. Recognises ./gradlew, gradlew, gradlew.bat, and gradle invocations. Surfaces unit-test report paths and shows a progress indicator for long-running tasks. Targets 75% savings (90% on test, 80% on build). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extract a new_gradle_command() helper that uses string literals in every Command::new() branch. The .semgrep.yml dynamic-command-execution rule rejects Command::new(var) — semgrep needs to statically audit the executable set. Same runtime behaviour: prefer ./gradlew (or gradlew.bat on Windows), fall back to gradle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces custom run_streaming / run_batch / run_passthrough / ProgressIndicator with the shared runner helpers used by cargo and other commands: - Build → runner::run_streamed with a BuildLineFilter implementing StreamFilter - Test/Lint/Connected/Deps → runner::run_filtered with the existing filter_* closures (filter_test, filter_connected, filter_lint, filter_dependencies) - Other / verbose flags → runner::run_passthrough(tool, args, verbose) Benefits inherited from runner.rs: ChildGuard zombie prevention, 10 MiB output cap, broken pipe handling, proper Result<i32> exit-code propagation. run() now returns Result<i32> like cargo_cmd / golangci_cmd. main.rs updated to forward the exit code with `?` directly (no manual `0` wrapper). All Command::new() invocations remain string literals (semgrep dynamic-command-execution rule). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
new_gradle_command() now uses resolved_command("gradle") for the two
fallback branches (Windows + Unix when no local wrapper is present),
matching how cargo / golangci-lint / etc. resolve system binaries.
Local wrappers (./gradlew, gradlew.bat) stay as string literals — they
are relative paths, not on PATH, and semgrep's dynamic-command-execution
rule needs literals here.
Net effect: PATHEXT-aware resolution on Windows (.CMD/.BAT shims work),
no behavioural change on Unix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(gradlew): Gradle support for Android/Kotlin developers
The `ok` / `fail` verdict line was emitted at the top of the filtered output, before errors and warnings. Consumers reading the tail of the stream — `| tail -N`, IDE log followers, agent watch/monitor loops, bounded-context-window readers — saw error noise followed by EOF with no verdict anywhere near the end, silently breaking any agent loop that gates the next step on build success. Reorder `format_build_output`, `format_test_output`, and `format_restore_output` to emit the body first, then the separator (only when the body is non-empty), then the verdict header last, matching native `dotnet` which ends with `Build succeeded.` or `Build FAILED.`. Add three regression tests using strict `output.lines().last()` plus a `tail -5` inclusion assertion so future regressions that append a trailing context line get caught. Closes #1574 Signed-off-by: Artiom Tofan <arto@queue-it.com>
- Separate warnings and errors into distinct sections. - Ensure status line is emitted last for clarity. - Maintain consistency with native `dotnet` output behavior.
- Improve formatting of output summaries for build, test, and restore commands. - Ensure status lines are emitted last for better stream consumption.
- Refactor warning formatting to improve consistency across output sections. - Ensure warnings are displayed correctly in all relevant output formats.
The file lives at docs/contributing/ARCHITECTURE.md, not at the repo root — both the nav header and Documentation section pointed to the wrong location. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Command wrappers like shadowenv (https://github.com/Shopify/shadowenv), `direnv exec .`, `nix develop --command`, `docker exec <container>`, `poetry run`, and `bundle exec` sit in front of every command in their respective project layouts. When rtk is driving a Claude Code / Gemini / Copilot / opencode hook, these wrappers defeat the rewrite — `rtk rewrite "shadowenv exec -- git status"` returns exit 1 (no rewrite) because the registry can't see past the wrapper to the inner `git status`. Every build tool, test runner, linter, and `git` invocation in such a layout stays uncompressed. rtk already solves exactly this shape for shell builtins via SHELL_PREFIX_BUILTINS (`noglob`, `command`, `builtin`, `exec`, `nocorrect`): strip the prefix, recurse on the inner command, re-prepend the prefix to the rewrite. This commit extends that pattern with a new [hooks].transparent_prefixes config field so users can register multi-word wrappers that don't change what runs, only how. [hooks] transparent_prefixes = ["shadowenv exec --", "direnv exec ."] With that config, `rtk rewrite "shadowenv exec -- cargo test"` becomes `shadowenv exec -- rtk cargo test` and exits 3 (ask, matching the existing ask semantics for inner commands). Implementation: - New `rewrite_command_with_prefixes(cmd, excluded, transparent_prefixes)` public entry. The original `rewrite_command(cmd, excluded)` is kept as a thin back-compat wrapper that passes an empty prefix slice, so the ~150 existing unit-test call sites don't need updating. - `transparent_prefixes: &[String]` threaded through `rewrite_compound`, `rewrite_segment`, `rewrite_segment_inner`. - In `rewrite_segment_inner`, a second strip loop runs after the SHELL_PREFIX_BUILTINS loop, with identical strip-recurse-reprepend semantics. Matching is whole-word via the existing `strip_word_prefix` helper (multi-word prefixes like `"shadowenv exec --"` are handled by-value — byte-level `starts_with` plus a space check). - Recursion is bounded by the existing MAX_PREFIX_DEPTH so pathological or self-referential configs cannot stack-overflow. - Production callers (`hooks/rewrite_cmd.rs`, `hooks/hook_cmd.rs`, `main.rs`) now load both `exclude_commands` and `transparent_prefixes` from config in the same `Config::load()` call — no new I/O. Tests (14 new): - config: 3 TOML roundtrip tests (present, missing, mixed with older fields). - registry: 11 behavior tests — strip/reprepend, unknown inner returns None, unmatched passthrough, composed with shell builtin, multiple prefixes configured, whole-word matching, empty-rest None, blank-entry skip, compound `&&` with prefix on both segments, excluded-inner returns None, recursion bounded. Design principles (from CONTRIBUTING.md): - Extensibility: extends an existing, documented pattern. - Zero Overhead: config load already happens on every hook invocation; this reads one additional vec. Empty `transparent_prefixes` is the default and adds one cheap loop-over-zero-elements on the hot path. - Correctness vs Token Savings: without this, hook rewrites silently miss every command run through a wrapper — exactly the kind of correctness gap the principle warns against.
When -bench is present, benchmark output is already compact and useful. Injecting -json causes the filter to discard benchmark results since they don't produce pass/fail events the JSON parser expects. Fixes #1609
fix: don't inject -json for go test -bench runs
inform user how to solve CI pr-target failing check
fix(cicd): pr-target clean msg + git app token
Three related issues prevented RTK from working with the Cursor
preToolUse hook on Windows. Each one alone produced the same
user-visible symptom (`Output: {}` and the original command running
unmodified), so they're fixed together:
1. Cursor preToolUse only enforces allow/deny — `permission: "ask"` is
accepted by the schema but ignored at runtime, so `updated_input`
rewrites could silently be dropped. Always return `allow` for
rewritten commands (deny rules still take precedence and are
evaluated before this point).
2. Cursor's preToolUse panel renders the JSON returned by the hook.
Without a `continue: true` field the panel collapses to
`Output: {}` even when the rewrite ran successfully, which makes
the hook look broken from the user's perspective. Every other
Cursor hook (`afterShellExecution`, `beforeSubmitPrompt`, `stop`,
...) returns `continue: true`; mirror that shape here so the panel
surfaces the actual `permission` and `updated_input` payload.
3. Cursor on Windows prepends one or two UTF-8 BOMs (`EF BB BF`,
sometimes doubled) to the JSON it pipes into the hook process.
serde_json refuses to parse BOM-prefixed input, so `run_cursor`
bailed out into the "no command" branch and returned `{}` for
every invocation. Strip leading BOMs before parsing. This was the
root cause of the long-standing "RTK silently no-ops in Cursor"
reports — the rewrite path was never reached.
Tests cover the flat allow output, `continue: true` on both single
and compound (`cd ... && git status`) rewrites, the BOM-strip on
double-BOM payloads matching what Cursor actually sends, and assert
that the legacy hookSpecificOutput envelope is not emitted.
Verified end to end on Windows by tracing real Cursor stdin payloads:
the hook now returns `{"continue":true,"permission":"allow",
"updated_input":{"command":"rtk git status"}}` for `git status` and
the panel renders it instead of `Output: {}`.
feat(init): add --dry-run flag to preview changes without writing
fix: correct ARCHITECTURE.md path in README links
refactor(warn): replace #[cfg] with cfg! macro to fix unused variable warning
Update telemetry documentation link to use 'master' branch
chore(ci): makes clippy pass, warnings included, mandatory
…-permission-ask fix(hooks): make Cursor preToolUse rewrites work and stay visible
Signed-off-by: Kayphoon <109347466+Kayphoon@users.noreply.github.com>
feat(hermes): add Hermes Agent support via rtk init --agent hermes
…ck-workflow fix(security): replace insecure tmp, lock git perm, set sha for actions
Contributor
Author
Release NotesLayer 1 — Install / Uninstall Lifecycle (pre-release artifact)
Layer 2 — Core Command Filters
Layer 3 — New Features & Recent Fixes
Layer 4 — System Filters
Layer 5 — Hook System, Analytics, Permissions
Release Decision
|
cicd: spelling branch fr->en
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Features
rtk init hermesfor automatic plugin installation #1031Fix
| tail -N#1574Others