Address PR #2609 review feedback:
- RC=3 in shell script now emits "permission": "ask" (future-proof)
- Add cursor_ask() and use it for all AskRewrite decisions
- Remove has_rules guard (unnecessary with ask semantics)
- Remove dead cursor_has_explicit_rules() function
Align cursor_has_explicit_rules() with the inline has_rules check in
run_cursor_inner_with_rules() — both now consider deny, ask, and allow
rules when determining whether the user has configured explicit
permission policies.
When no explicit permission rules exist in ~/.cursor/cli-config.json
(the default for fresh installs), every command gets Default verdict
which maps to AskRewrite. The Cursor hook only handled AllowRewrite,
silently dropping all rewrites and making RTK non-functional.
Now treat AskRewrite as allow when no rules are configured — Cursor
has no ask-the-user UX, so deferring is indistinguishable from
dropping. When explicit rules exist, preserve the conservative
behavior of deferring mixed/unmatched commands.
Also fix the legacy shell script (rtk-rewrite.sh) which treated
exit code 3 (ask) as failure via || short-circuit.
Fixes#2372
`Commands::Run` used `status.code().unwrap_or(1)` which silently
maps signal kills (SIGTERM, SIGKILL, OOM) to exit code 1 instead
of the POSIX-conventional 128+signal. Use the existing
`exit_code_from_status()` helper already used by Commands::Proxy.
Fixes#2680
- drop the synthetic "search failed with exit code N" line (an rtk addition)
- surface the engine stderr verbatim in every path, incl. partial match + error
- remove the now-vestigial is_grep_error_exit (the "0 matches" it guarded is gone)
- error/exit matrix tests vs real grep/rg (#2465: an error is never a silent no-match)
- grep_compress_test.rs -> search_compress_test.rs (tests the shared grep/rg filter)
- binary match skipped as noise by default, -a opts back in
- fix parse_flags doc: -I is an intentional binary skip, not a no-op
- leading ^ anchor must stay scoped to the path (tenequm)
- line ending in `:` keeps full content (BTCAlchemist)
- rg surfaces a regex parse error as exit 2, not a silent 0 (jhagberg)
- line number always (the openable position); filename only when grep/rg prints one (multi-file, dir, -r, -H)
- single file and stdin equal `grep -n` byte-for-byte: no synthetic filename, header, or lossy truncation
- read piped stdin instead of injecting `.`; rg no longer searches the cwd on a pipe
- pattern-less and format-flag commands run verbatim (rg --type-list, --files, grep -c/-o)
- grouped/capped form only when capping actually shrinks the output
- byte-for-byte tests vs grep -n across flags, pipes and edge cases (incl #1436)
- grep runs grep, rg runs rg: drop the substitution, forced --no-ignore-vcs, and BRE-to-rg translation
- add `rtk rg` command (native ripgrep, sharing the same output filter)
- split rewrite rule: grep to rtk grep, rg to rtk rg
- record the agent's real command in tracking (was synthesized as "grep -rn")
- emit nothing on a clean no-match (never-worse parity with the shared guard)
- rename grep_cmd.rs to search.rs, now hosting both engines
- cover engine faithfulness, ignore semantics, and rg savings with issue-referenced tests
- benchmark the grep and rg paths
docker ps/ps -a/images ran a plain command for the raw baseline and a
separate --format command for parsing, capturing the plain run with
unwrap_or_default(). A failed plain run while --format succeeded left
raw empty, and never_worse("", summary) dropped real output.
Run the agent's actual command first as the source of truth: on failure
show its output/exit faithfully; on success use its stdout as raw and
treat --format as a best-effort parse aid that falls back to raw. The
guard stays pure and always sees a real baseline. Parsers unchanged.
Compare filtered output against the raw the agent's command would
emit (numbered for -n), not against unnumbered content, so the
guard keeps requested line numbers instead of dropping to plain.
Masking env secrets is not what core RTK (OSS) is for -- it compresses command
output, it does not redact what the command itself already reveals. It also
broke raw-vs-filtered consistency, which the never-worse guard relies on.
RTK could emit more tokens than the underlying command on small inputs:
filters that add headers, summaries, re-indentation, or a tee hint, plus
synthetic no-result messages ("0 matches", "No stashes", "[docker] 0
containers") printed where the raw command emitted nothing. Both break the
Transparency principle and inflate tokens instead of saving them.
- core::guard::never_worse(raw, filtered) returns raw when the filtered form
has more tokens (reuses tracking::estimate_tokens), so RTK output is never
larger than the real command.
- runner::emit_guarded(filtered, hint, raw) composes body + tee hint, guards
the whole, prints, and returns what was shown so printed == tracked.
- run_captured_filter guards the run_filtered* family centrally; per-site
guards cover the remaining single-string filters.
- On empty raw, emit empty and preserve the exit code instead of a synthetic
no-result message (the messages were cosmetic with no dependents; #2461
reports the grep one as actively harmful).
- git stash show now propagates its exit code instead of masking a real
failure as Ok(0).
Resolves#2551.
Review feedback on #2550: the flag-shape tests all used standalone flags, leaving the exact #2543 repro -- a bundled -rln/-ln files-with-matches cluster -- unexercised. Assert rtk grep -rln <pat> <dir> lists matching files instead of a false '0 matches'.
Per review: avoid signal loss that pushes agents to retry with
`rtk proxy`. pulumi-stack now keeps the full stack identity block
(Owner:, Pulumi version used:) alongside the header and Last updated:.
Only interactive prompt and help-footer noise is stripped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The grep argument-parsing rework routed many flag shapes through the
grouping reparse, which silently dropped any line it could not parse and
returned a success exit. Common invocations produced wrong or empty
output -- and grep-only syntax like `--include` failed outright -- which
pushed agents back to unfiltered `rtk proxy grep`.
Every valid grep invocation now produces correct output:
- Context (`-A`/`-B`/`-C`) is grouped and compressed instead of dropped;
the header counts matches only and context lines are shown.
- Output the reparse cannot represent (`-N`, `-I`, `--color`, `-p`,
`--heading`, binary-file notices, ...) is passed through verbatim
instead of reported as a false "0 matches".
- Format/shape flags (`-c`, `-l`, `-o`, `-Z`, `--column`, `-b`,
`--vimgrep`, `--null-data`) pass through cleanly with no NUL leak.
- grep-only syntax (`--include`, `--exclude`, ...) works: ripgrep is the
fast path and rtk falls back to system grep when ripgrep rejects a flag.
- rtk grep never emits more than plain grep: if grouping is not smaller
than `file:line:content`, the raw form is shown.
Resolves#2543.
Stop stripping `Current stack is X:` and `Last updated:`. pulumi stack
is read-only (exit 0) so there's no tee fallback; dropping the active
stack identity was irreversible signal loss. Keeping the header also
fixes the orphaned indented `Managed by <user>` line.
Owner: and Pulumi version used: remain stripped as noise.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test_needs_raw_fallback dropped the raw stdout prepend whenever every
parsed failure carried detail, but never checked the list was complete
against summary.failed. A run reporting 5 failures but parsing only 3
detailed blocks would silently lose the 2 unparsed failures.
Add a `failed_tests.len() < summary.failed` clause so the raw fallback is
kept when the structured section is numerically incomplete. Add a
regression test and fix an inconsistent existing fixture (failed: 5 with a
single parsed entry) that the new clause correctly flagged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Explicit Vitest reporters can produce successful output that exceeds the passthrough cap. Track truncation as structured state and use force_tee_hint so the full verbose output remains recoverable.
On failing `dotnet test` runs the orchestrator prepended the full raw
stdout ahead of the filtered summary. The `Failed Tests:` section already
reproduces each failure (name + message + clipped stack) parsed from
TRX/console, so every failure was printed twice — inflating output +65%
vs raw, scaling linearly with failure count (issue #2501).
Gate the raw-stdout prepend behind `test_needs_raw_fallback`: skip it when
the structured section carries detail, keep it when the filter is blind
(no failures parsed, or a parsed failure has empty detail — e.g. a
self-closing <UnitTestResult> with no <ErrorInfo>, or a build failure /
crash where nothing reaches failed_tests).
Extracts two pure, unit-tested fns (`test_needs_raw_fallback`,
`compose_failure_output`) so the orchestration decision is covered without
running dotnet.
Closes#2501
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
run_worktree list mode never checked result.success() — a failed
`git worktree list` (e.g. run outside a repo) was flattened to empty
output + exit 0. The has_action branch already guards on success; apply
the same guard to list mode and surface git's error with its exit code.
Part of #2497