Repository navigation
Conversation
MinimalFilter used trimmed.contains() to detect block-comment openers, so any code line containing /* as a substring (string literals, globs like "src/*.rs", regex patterns) was dropped — and when no */ followed on the same line, in_block_comment leaked and silently swallowed the rest of the file. Match openers with starts_with() so only genuine comment lines enter block-comment state. Fixes #2385
rtk init -g --agent cursor fell through to the shared init path with install_claude derived only from !opencode, so it created RTK.md, CLAUDE.md, and patched settings.json under ~/.claude in addition to patching .cursor/hooks.json. On machines without ~/.claude it failed outright when creating the temp file. Two fixes: - Gate install_claude on the absence of a sibling agent target (Cursor, Windsurf, Cline) so only the default or explicit Claude target installs Claude Code files. - Create ~/.cursor before patching hooks.json; atomic_write places its temp file in the target's parent, which failed on a fresh machine where the directory did not yet exist. Closes #2097
Python has no block comments, but the minimal filter treats """ as one. Two things go wrong. A line like `QUERY = """` contains the delimiter without starting with it, so it is taken for a block-comment opener. The same """ then satisfies the closing test on that one line, so the line is dropped and its string body is left behind as loose text. A single-line docstring such as """Module doc.""" toggles the docstring flag once and never back, so the filter stays in docstring mode for the rest of the file and stops stripping comments entirely. Give Python a string-aware path instead. Minimal keeps docstrings, so the only thing to remove is # comments and the only state needed is whether we are inside a triple-quoted string. Delimiter kinds are tracked separately so a ''' inside a """ string does not close it. Other languages keep the existing block-comment walk untouched. Refs #3260
`rtk discover -p <filter>` matches the filter against project directory names with a case-sensitive `contains`, so on Windows it can come back empty even when the project is there. NTFS is case-insensitive and Claude Code builds the folder name from whatever cwd casing it was launched with, so one project can end up under both `E--proj` and `e--proj` while `std::env::current_dir()` hands back an uppercase drive letter. Compare case-insensitively under `cfg!(windows)`. Unix behavior is unchanged. Refs #2919
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 7. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](actions/setup-go@v5...v7) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/github-script](https://github.com/actions/github-script) from 7.1.0 to 9.0.0. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](actions/github-script@f28e40c...3a2844b) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: 9.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/stale](https://github.com/actions/stale) from 9 to 11. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](actions/stale@v9...v11) --- updated-dependencies: - dependency-name: actions/stale dependency-version: '11' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](softprops/action-gh-release@v2...v3) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
`format!("Failed to run {} pip list", base_cmd)` was written for the
`uv pip` branch, so on the plain-pip path it printed "pip pip list".
Add `prog_label()` returning "uv pip" or "pip" and use it in all six
verbose/error strings. The executed argv was already correct.
Fixes #4050
`test_git_status_not_a_repo_exits_nonzero` matched git's English "not a git
repository" on stderr, so it failed in any other language -- `git status` is a
passthrough and keeps the user's locale by design. It now asserts what RTK
actually owns there: a non-zero exit, the message on stderr, nothing on stdout.
The quieter half: `!out.contains("Is a directory")` in search_faithful_test
passed vacuously wherever grep speaks another language, testing nothing. The
engine-spawning helpers pin LC_ALL for both sides of a comparison, so those
assertions mean the same thing everywhere.
The branch tests ran `git branch` in whatever repo the contributor was sitting
in and deleted it afterwards; the broken-pipe test read that repo's log, and
outside a repo it stopped exercising the pipe at all. All three take a
throwaway repo from the new test_support helpers, which also neutralize
GIT_CONFIG_GLOBAL/SYSTEM so local aliases and init.defaultBranch stay out.
Verified against fr_FR, de_DE, an empty git config and an injected
RIPGREP_CONFIG_PATH.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Its match_command, `^(gradle|gradlew|\./)gradlew?\b`, needs a doubled token (`gradlegradle`) or a literal `./gradle…`. Both call sites reduce argv[0] to its basename before the lookup — `run_fallback` in main.rs and `strip_absolute_path` on the hook rewrite path — so the `./` branch is unreachable too. The filter has matched nothing since it landed: the basename logic arrived in adda253 (2026-03-10), the file in 8fae5b0 (2026-03-18). Deleting it changes no output. `src/discover/rules.rs` already routes the whole gradle family (`gradle`, `gradlew`, `./gradlew`, `gradlew.bat`) to `rtk gradlew` and the Rust gradlew_cmd, and `gradle … bootRun` is filtered by spring-boot.toml. Widening the regex instead would have shadowed spring-boot.toml — first match wins, ordered by filter name — and pulled gradle-profiler and gradle.bat into a head-truncating filter. Making TOML filters reachable from the default passthrough is a separate design question, tracked in #3904. Refs #2708
`rtk grep -l`, `grep -L`, `rg -l`, and `rg --files` print every path in full and record 0/0 savings. Agents search from absolute roots, so each line repeats the same long directory prefix. Fold that prefix into a `<prefix> (N files)` header and print each path's tail. The transform is lossless, keeps engine order, cuts only on whole components, and runs under `never_worse`. It applies only to a bare file list: any other shape flag (`-c`, `-Z`, `--null`, `--json`, `-q`) changes the line and leaves the list verbatim. Streaming stdin passthrough is untouched. Track the fold with real sizes so it shows in `rtk gain`; unchanged passthrough keeps recording 0/0. Follow-up to #2630, which targeted the pre-#2641 grep_cmd.rs.
`Ok(2)` at the read-failure site does not say which of diff's three exit codes it is. The constant states the contract the tests and the docs now depend on: 2 means an operand could not be read, not that the files differ. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
A folded file list carries its shared prefix in the header line, so `grep -rl foo src | tail -3` rewritten to `rtk grep` returned tails with no prefix. List forms (-l/-L/--files, per `is_bare_file_list`) are now left raw in producer position; other grep/rg forms rewrite as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… test A hook group whose `matcher` is JSON `null` was counted as not covering the tool, so `rtk init` appended a second RTK group next to an existing one. Treat `null` like an omitted matcher, as develop did. The Copilot integration test ran `rtk hook copilot` from a temp dir whose project-root walk could reach a `.claude/settings.json` on the test machine, letting its permission rules change the response. Give the temp dir its own `.claude` so the walk stops there. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The module map in the search-strategy rule still counted 63 TOML filter configs; src/filters/ now holds 62.
Brings the branch onto the current develop so CI's semgrep baseline is the develop tip rather than the July merge-base; no source changes beyond the merge itself.
…ions fix(discover): distinguish zero-session scans
fix(discover): match Claude project dirs case-insensitively on Windows
…4050 fix(pip): stop doubling the tool name in pip messages
…t like the sibling agents On develop the rules file handed the agent the full awareness text; the plugin bundle carried none, so a session got the rewrite and nothing that says output is condensed or how to recover with rtk proxy. The plugin now holds rules/AGENTS.md with the file awareness.level selects, plain markdown with no frontmatter, which Antigravity applies whenever the plugin is enabled. A re-run rewrites it, so changing the level takes effect. The plugin files go through the shared atomic_write, and plugin.json, the file that makes Antigravity discover the directory, is written last: a write that fails halfway leaves no plugin rather than one missing its rules. The install output tells the user to restart Antigravity, which only discovers a plugin directory on startup. Uninstall reports like Factory Droid: a "not installed (nothing to remove)" line when there is no plugin, otherwise a header and the removed items, and the dry-run footer; the helper that removes the directory prints nothing. README's agent table and the configuration guide no longer describe the rules file, and the "<1.5ms" figure goes: the hook measures about 7 ms end to end. Process-level tests pin the printed lines and that both dry runs change nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(hook): add native Google Antigravity plugin lifecycle and hook support
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](actions/upload-artifact@v4...v7) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
…p/actions/upload-artifact-7 chore(deps): bump actions/upload-artifact from 4 to 7
…p/softprops/action-gh-release-3 build(deps): bump softprops/action-gh-release from 2 to 3
…p/actions/github-script-9.0.0 build(deps): bump actions/github-script from 7.1.0 to 9.0.0
…p/actions/stale-11 build(deps): bump actions/stale from 9 to 11
…p/actions/setup-go-7 build(deps): bump actions/setup-go from 5 to 7
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v4...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
The raw side was measured with 2>&1 and the rtk side with 2>/dev/null, so the two columns were not the same measurement. rtk forwards the stderr it does not filter, and dropping that stream books it as if it had been removed. It also hid the failure mode this fixture is meant to catch. A filter that goes silent while the tool reports on stderr scored as a saving; the golangci-lint row read 157 -> - and failed the run for producing nothing, with no way to see that the diagnostic had gone to the other stream all along. For an identical measurement rtk is better or equal by construction: stderr is common to both sides, so never-worse reduces to `filtered <= stdout`. Measuring stderr also shows when the tool under test is missing. The CI runner has no rg, so the rg rows compared the shell's "command not found" with rtk's own error and went negative. The benchmark job now installs ripgrep, and the rg section is skipped when rg is absent, as the tree section already is. The run reports 0 negative and 0 fail both with rg installed (rg fn 125360 -> 4083) and without it (section skipped). The good/warn split moves by a row or two between machines and with what is installed, so it is not a property of this change. Verified in golang:1.27 that the go rows read 220 -> 35 GOOD on v2.13.2, and 157 -> 157 -- a tie, not a red run -- if the v2 install were ever reverted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test: stop tests depending on the contributor's locale and repo
`cargo test` wrote into the contributor's own files (tracking database, tee spool, telemetry salt, agent hooks and settings) and took its results from their machine: agent settings, exported RTK_* and agent variables, git configuration and repository variables, files above TMPDIR. A test build now resolves every user location through `core::user_dirs` and reads the environment only through `core::user_env`, both answering from a per-process scratch directory and per-thread values a test sets. Spawned rtk children get the same redirect through the environment, and every git the suite runs is isolated from the developer's configuration and repositories. #3826's helpers move into `test_isolation`, so there is one `rtk_command`. Scans refuse a test that names the rtk binary outside the two constructors, and a direct `dirs::`, environment or working-directory read outside `user_dirs`/`user_env`. Production behaviour is unchanged; every redirect sits behind cfg(test). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bench: measure both sides of every row on stdout+stderr
…p/actions/checkout-7 chore(deps): bump actions/checkout from 4 to 7
Notifies an external pipeline when a pull request from develop to master is opened or updated, so the release candidate is analysed automatically. The job is a single authenticated request and nothing else. It checks out no code, declares no permissions, and exits quietly when the repository has no pipeline configured, so forks are unaffected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A fork branch also named develop satisfied the previous condition. Secrets are withheld from fork pull requests so the job exited without acting, but it still started and consumed runner time, and the safeguard was implicit. Comparing the head repository with the current one makes the intent explicit and stops the job from starting at all in that case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix(tests): keep the test suite and the developer's environment apart
aeppling
left a comment
There was a problem hiding this comment.
Review — PR #4228 (develop → master, v0.51.0) @ 4c8a255e
Status: BLOCKED — 1 critical regression. All other layers green: 4076 unit tests, 11/11 changelog features verified on the release binary, real install/uninstall with rc.482 artifacts, exit-code propagation, git status/diff/find/read output byte-identical to v0.50.0.
🔴 BLOCKER — regression (#4125): single-string commands broken in err / test / run / summary
Any command passed as one quoted string — the form that works on v0.50.0 and that existing deployed instructions produce — now fails with exit 127 and rtk: <whole string>: command not found. Exit codes are also wrong ('true && false' returns 127 instead of 1). The error gives no indication of the new syntax, so an agent with pre-upgrade instructions dead-ends and retry-loops.
Reproduce (compare v0.50.0 vs a binary built from this PR):
rtk err 'echo hello' # 0.50.0: exit 0 → PR: exit 127
rtk err 'true && false' # 0.50.0: exit 1 → PR: exit 127
rtk err 'cargo --version 2>&1' # 0.50.0: exit 0 → PR: exit 127
rtk test 'ls /' # 0.50.0: exit 0 → PR: exit 127
rtk test 'cd /tmp && pwd' # 0.50.0: exit 0 → PR: exit 127
rtk summary 'echo a && echo b' # 0.50.0: exit 0 → PR: exit 127
rtk run 'echo one two' # 0.50.0: exit 0, prints "one two" → PR: exit 127
rtk err 'echo x | grep x' # 0.50.0: exit 0 → PR: exit 127Multi-arg forms (rtk err echo hello, rtk run echo one two) work on both.
…orm shell Direct execution (#4125) made every pre-existing single-string invocation of err/test/run/summary — the shape all deployed awareness files teach — die with 127 'command not found' and no way for an agent to self-correct. Restore exactly that set: when no --shell is given, there is exactly one argument, it resolves to nothing (127, not an existing-but-unexecutable 126 path), and it is empty or contains a shell metacharacter, run it through the platform shell as the interposed sh -c used to, with a one-line stderr note teaching the current syntax. Multi-argument argv fidelity, resolvable programs, bare-word 127s and 126 classifications are untouched by construction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Targeting master from develop is a weak signal on its own. The release pull request carries the next-release label, applied by the release bot, which states the intent explicitly rather than inferring it from branch names. Also reacts to the labeled event, so a pull request opened before the label is applied still starts the analysis once it arrives, and ignores the labelling of anything else. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ci(transparency): trigger transparency analyze
fix(shell): run unresolvable single-string commands through the platform shell
Feats
Fix
rtk ruff checkon a clean tree prints[]:never_worsepicks the injected JSON over the filter's summary #4218) #4266Other