Commit Graph

1256 Commits

Author SHA1 Message Date
patrick 129104e388 docs(windows): document native binary hook support
Native Windows has had full auto-rewrite support since v0.37.2 via the
`rtk hook claude` native binary command (no bash/jq/Unix shell). The
README still described the pre-v0.37.2 "CLAUDE.md fallback" behavior,
which is now inaccurate and a recurring source of confusion (#330).

- Rewrite the Windows section: native binary hook is the primary path,
  WSL secondary; drop the "limited support / CLAUDE.md fallback" framing
- Add an upgrade note (re-run `rtk init -g` to migrate off rtk-rewrite.sh)
- Add a ripgrep prerequisite note (`rg` on PATH)
- Fix the install note and Supported AI Tools table (bash -> native binary)

Refs #330

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 22:54:40 +02:00
Nicolas Le Cam b52e02b437 chore(scripts): switch benches to mockhttp.org as httpbin.org is struggling at the moment dev-0.44.0-rc.304 2026-07-01 13:35:05 +02:00
Nicolas Le Cam 32dda24e5b Merge pull request #2751 from guyoron1/fix/cjk-emoji-char-boundary-panic
fix(parser): use byte offsets instead of char indices in extract_json_object
2026-07-01 13:31:19 +02:00
Nicolas Le Cam 1052c1c702 Merge pull request #1649 from iliaal/feat/php-tooling
feat(php): consolidated PHP tooling (php, artisan, phpunit, phpstan, pest, paratest, ecs, pint)
dev-0.44.0-rc.302
2026-07-01 13:16:49 +02:00
aesoft ce30f378e3 Merge pull request #2771 from rtk-ai/fix/cicd/pr-targer-next-release
fix(cicd): next release pr target fork compatible
2026-07-01 13:15:45 +02:00
Ilia Alshanetsky d67fc28884 test(php): add token-savings assertion to paratest per cli-testing guide
.claude/rules/cli-testing.md marks token-savings verification as a 🔴
requirement for filters. Add a ≥60% savings assertion over a realistic
paratest run (banner + config + parallel-worker progress → one-line
summary), alongside the existing behavioral tests.
2026-07-01 06:18:31 -04:00
Ilia Alshanetsky 5e34f61f9c Merge remote-tracking branch 'origin/develop' into feat/php-tooling
# Conflicts:
#	src/main.rs
2026-07-01 06:17:07 -04:00
Ilia Alshanetsky fe755957e1 test(php): add paratest unit tests to satisfy test-presence gate
paratest_cmd.rs lacked the mandatory #[cfg(test)] module, failing the
check-test-presence CI gate. Add tests exercising the ParaTest-specific
path in filter_test_runner_output: banner + "Random Seed:" + dot progress
stripped, result summary and failure details preserved.
2026-07-01 06:11:33 -04:00
Adrien Eppling 6e34bbaa98 fix(cicd): next release pr target fork compatibel 2026-07-01 11:14:54 +02:00
guyoron1 28948d9176 Merge branch 'develop' into fix/cjk-emoji-char-boundary-panic 2026-07-01 11:58:22 +03:00
aesoft 8cdc861ede Merge pull request #2768 from guyoron1/fix/uv-transparent-prefix-conflict
fix(uv): remove uv run from transparent prefixes to fix rewrite conflicts
dev-0.44.0-rc.300
2026-07-01 10:52:47 +02:00
guy oron 0632d838ed style: apply rustfmt to BUILTIN_TRANSPARENT_PREFIXES 2026-07-01 11:12:38 +03:00
guy oron 96f94222cb fix(uv): remove uv run from transparent prefixes to fix rewrite conflicts
uv run was listed in both BUILTIN_TRANSPARENT_PREFIXES and RULES,
causing the transparent prefix check to win and return None when the
inner command (e.g. python script.py) wasn't RTK-supported. Now all
uv run commands route through the RULES-based rtk uv path, which
delegates to uv_cmd.rs for filtering.
2026-07-01 11:12:38 +03:00
guy oron 7e42d929e4 fix(uv): pass guard_raw arg to print_with_hint after signature change 2026-07-01 11:12:38 +03:00
guy oron 27f9739b29 fix(parser): use byte offsets instead of char indices in extract_json_object
extract_json_object() used char indices from enumerate() as byte offsets
when slicing the input string. For multi-byte UTF-8 characters (CJK,
emoji), this causes a 'not a char boundary' panic because char index !=
byte offset.

Switch from Vec<char> + enumerate() to char_indices() which yields
correct byte offsets. Add regression tests for CJK values, emoji values,
CJK prefixes, and mixed multi-byte nested JSON.

Fixes #2509
2026-07-01 08:59:36 +03:00
Nicolas Le Cam bd10b002d0 Follow up on #2609:
- Fix documentation, Cursor now uses native rust binary hook
- Fix rtk-rewrite.sh shell script to fail on any exit code except 0 and 3
- Test that "continue": true is also present for "permission": "ask" and sync shell rewrite hook
2026-06-30 22:18:39 +02:00
aesoft a5f0774d0e Merge pull request #1405 from alexanderkreidich/fix/1379-uv-run-support
feat: add uv run support
2026-06-30 22:16:16 +02:00
Ilia Alshanetsky 5d2928c64e perf(php): cache composer_bin_dirs to avoid per-segment file reads
composer_bin_dirs() re-read composer.json and the env on every call; the
rewrite hot path queries it several times per command segment via
normalize_php_tool_command (both classify_command and rewrite_segment_inner).
Resolution is constant for a process, so cache it in a OnceLock.
2026-06-30 08:18:27 -04:00
Ilia Alshanetsky 50a8743041 fix(pipe): anchor phpunit auto-detection to the leading banner
Matching only "by Sebastian Bergmann" misrouted any LICENSE, composer
metadata, or git log mentioning the author to the phpunit filter. Require
the input to start with "PHPUnit " as well.
2026-06-30 08:18:27 -04:00
Ilia Alshanetsky 28366ce17b fix(php): drop bogus pest.php check in test-runner detection
Pest has no root `pest.php` file — its bootstrap lives at tests/Pest.php
and its canonical marker is the vendor/bin/pest binary. The root-level
check never matched real Pest projects and false-positived on unrelated
utility files, misrouting PHPUnit-only projects to the Pest filter.
2026-06-30 08:18:27 -04:00
Ilia Alshanetsky 7cc46b4676 fix(php): detect phpstan analyse after global flags; avoid duplicate format
`phpstan -c phpstan.neon analyse src/` put a global option before the
subcommand, so the first-arg-only check missed it and the run fell back
to unfiltered passthrough. Scan all args for analyse/analyze.

Replace the has_custom_format bool with a 3-state ErrorFormat enum so an
explicit `--error-format=json` is preserved without appending a second
`--error-format json`. Strip ANSI in the text fallback for `--ansi`.
2026-06-30 08:18:27 -04:00
Ilia Alshanetsky 8825480b7a fix(php): strip ANSI in phpunit filter and split errors from failures
filter_phpunit_output matched on raw bytes, so `--colors=always` output
(colorized "OK ("/"FAILURES!"/"Tests:" lines) defeated every anchor and
counts were lost. Strip ANSI first, mirroring the other PHP filters.

Also report PHPUnit errors (thrown exceptions) distinctly from failures
(assertion mismatches) instead of lumping both under "failures".
2026-06-30 08:18:27 -04:00
Karol Zalewski 487a2e7b2c fix(openclaw): handle exit code 3 from rtk rewrite
Interpret all RTK rewrite exit codes and respond appropriately:
- Exit 0 (Allow): auto-apply rewrite
- Exit 1 (Passthrough): no RTK equivalent, pass through unchanged
- Exit 2 (Deny): block the call entirely
- Exit 3 (Ask): rewrite available, require user approval via
  requireApproval with allow-once/deny decisions

Uses execFileSync (not execSync) to avoid shell injection. Returns
a [string | null, RewriteVerdict?] tuple from tryRewrite so the
before_tool_call hook can react to each verdict type.

"allow-always" omitted from allowedDecisions because OpenClaw does
not auto-persist approval for plugin hooks — see:
https://docs.openclaw.ai/plugins/plugin-permission-requests#troubleshooting

Closes #2202
dev-0.43.1-rc.297
2026-06-30 13:10:12 +02:00
Ilia Alshanetsky 0cc15dc448 fix(php): default pint applied_fixers when key is absent
PHP-CS-Fixer omits the fixers key in dry-run/diff modes (it is optional
in the JSON reporter), so a valid report like {"files":[{"name":"x.php"}]}
failed the whole parse and fell back to raw output. Mark the field
#[serde(default)] so a missing key deserializes to an empty vec.
2026-06-29 19:45:47 -04:00
guy oron 36dd8f2479 fix(hook): use ask action in audit log for AskRewrite verdict dev-0.43.1-rc.296 2026-06-29 14:18:05 +02:00
Guy Oron a0c16ef0bc fix(hook): use ask permission for AskRewrite in Cursor hook
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
2026-06-29 14:18:05 +02:00
guy oron 33623258b8 fix(hook): include ask rules in cursor_has_explicit_rules check
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.
2026-06-29 14:18:05 +02:00
guy oron ff0b9ac28c fix(hook): handle AskRewrite in Cursor hook when no rules configured
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
2026-06-29 14:18:05 +02:00
Ilia Alshanetsky 6117bb4deb refactor(php): standardize tool regexes and normalize invocation in rewrite
The six Composer-tool rules had inconsistent patterns, and their
rewrite_prefixes only worked because classify normalizes the command while
rewrite stripped literal prefixes off the raw text. A form the pattern
accepted but the prefix list missed (e.g. ./bin/phpunit) would classify yet
silently fail to rewrite.

rewrite_segment_inner now normalizes the leading invocation for these tools
(php wrapper, ./, vendor/bin, composer bin-dir) the same way classify does, so
the prefix list collapses to the residual canonical forms: bin/<tool> + bare
name for phpunit/phpstan, bare name for pest/paratest/ecs/pint. Patterns
standardized to ^(?:php\s+)?(?:\./)?(?:(?:vendor/)?bin/)?<tool> for
phpunit/phpstan and ^(?:\./)?(?:vendor/bin/)?<tool> for the rest. Adds a
form-coverage test asserting every accepted spelling maps to one rewrite.
2026-06-28 19:51:39 -04:00
Ilia Alshanetsky b5c3f7fac5 perf(php): resolve cwd once in pint output instead of per file
short_path() called current_dir() on every file (up to MAX_FILES_SHOWN per
invocation). Hoist the cwd prefix out of the loop and strip it inline.
2026-06-28 19:42:55 -04:00
Ilia Alshanetsky 128d04f779 fix(php): anchor phpunit failure-heading detection to the "N) " format
is_numbered_failure_heading matched any line starting with a digit and
containing ')', which split a failure block on detail lines like
"5 of 10 assertions passed in Foo::bar()". Anchor to `^\d+\) \S` via a
lazy_static regex. Adds a regression test.
2026-06-28 19:42:55 -04:00
Ilia Alshanetsky 07c231ee4c fix(php): address phpstan review feedback (resolution, text fallback, path compaction)
- run() now uses php_tool_command("phpstan") instead of hardcoding
  vendor/bin/phpstan, so COMPOSER_BIN_DIR and composer.json's config.bin-dir
  are respected, matching every other php tool in this module.
- filter_phpstan_text only treats shell-level "binary missing" lines as a
  failure. The previous contains("not found") matched real analysis messages
  ("Class X not found") and swallowed the summary; also dropped a stray Ruby
  LoadError string ("cannot load such file"). Adds a regression test.
- compact_php_path reduced to last-two-components for all frameworks, dropping
  the Laravel-specific prefix list. Tests updated to the compacted output.
2026-06-28 19:42:55 -04:00
Ilia Alshanetsky 8eae6b7334 fix(php): route php_tool_command through resolved_command
The `.semgrep.yml` `dynamic-command-execution` rule forbids `Command::new` on a
variable. `php_tool_command` built the command directly from the resolved tool
path; route it through `resolved_command` (the sanctioned PATHEXT-aware
constructor) instead. Clears both blocking findings with no behavior change.
2026-06-28 19:42:55 -04:00
Ilia Alshanetsky 4a37246a05 fix(php): classify php subcommands in the PASSTHROUGH list
develop's `test_every_subcommand_is_classified` requires every CLI subcommand
to appear in `RTK_META_COMMANDS` or `PASSTHROUGH`. The consolidated php
subcommands (php, phpunit, phpstan, pest, paratest, ecs, pint) wrap real
tools, so they belong in `PASSTHROUGH`. Without this the PR-into-develop merge
fails the test.
2026-06-28 19:42:55 -04:00
Ilia Alshanetsky 88e56cef85 fix(php): rewrite ./vendor/bin/<tool> form for phpunit/pest/paratest/ecs/pint
`./vendor/bin/<tool>` is the common Laravel invocation form. classify_command
normalizes the leading `./`, so these classify as supported, but the rewrite
strips literal `rewrite_prefixes` from the raw command and the five rules only
carried `vendor/bin/<tool>` and bare `<tool>`. So `./vendor/bin/pint` ran raw
with no compression while `vendor/bin/pint` rewrote to `rtk pint`. phpstan
already carried `./vendor/bin/phpstan`. Add the `./vendor/bin/` prefix to the
other five, with a regression test.

Reported by @evaldnet (verified against pint 1.29.1, phpstan 2.1.40).

Co-authored-by: Aaron Florey <azza@jcaks.net>
Co-authored-by: Eli White <1153183+EliW@users.noreply.github.com>
Co-authored-by: Benjamin LETELLIER <bletellier@audencia.com>
Co-authored-by: Luciano <vandi.luciano@gmail.com>
2026-06-28 19:42:55 -04:00
Ilia Alshanetsky 0b75581f6b fix(php): align pint/phpstan parsers with current tool schemas
pint: Pint >=1.14 renamed JSON keys name->path and appliedFixers->fixers.
The struct fields were required with no aliases, so serde rejected current
output and the filter fell back to raw (no compression). Add backward-
compatible aliases so both schemas parse.

phpstan: the "ok" gate and summary line read totals.errors, which counts
only non-file-specific (global) errors. A normal failing run reports
errors=0 with the count in file_errors, so runs with real errors were
reported as "phpstan: ok", silently hiding failures. Gate on both counts
and report file_errors in the summary.

Both regressions slipped past the suite because the fixtures set
errors == file_errors (phpstan) and used the old key names (pint). Added
regression tests using the current-version schemas.

Reported by @evaldnet (verified against Pint 1.27.1, PHPStan 2.1.40).

Co-authored-by: Aaron Florey <azza@jcaks.net>
Co-authored-by: Eli White <1153183+EliW@users.noreply.github.com>
Co-authored-by: Benjamin LETELLIER <bletellier@audencia.com>
Co-authored-by: Luciano <vandi.luciano@gmail.com>
2026-06-28 19:42:55 -04:00
Eli White 214a79a9ff feat(pipe): expose PHP tool filters as stdin pipe filters
The PHP runners (phpunit, pest/paratest, ecs, phpstan, pint) only
applied their compact filters when rtk itself launched the process.
Output produced elsewhere — most commonly a tool run inside a Docker
container and piped back to the host — bypassed them entirely, since
the visible command is `docker ...`, not the PHP tool.

Wire the existing filter functions into `rtk pipe`:

- resolve_filter: phpunit, pest|paratest|php-test, ecs, phpstan, pint
- phpstan/pint pipe wrappers sniff JSON-vs-text by content, since the
  runners force --format=json but piped output may be either
- auto_detect_filter: route the "by Sebastian Bergmann" banner to the
  phpunit filter (no -f needed)
- bump the four backing fns to pub(crate)

Also fix filter_phpstan_text matching the summary line case-sensitively
("found"), which missed phpstan's actual "[ERROR] Found N errors".

Tests: phpunit banner auto-detect, phpstan case-insensitive summary.
2026-06-28 19:42:55 -04:00
Ilia Alshanetsky c37eed9628 fix(test): adapt new rewrite_command signature in php-tooling tests
Upstream develop changed `rewrite_command` to take 3 args
(cmd, excluded, transparent_prefixes). The PHP tooling tests that
don't care about transparent prefixes now use the existing
`rewrite_command_no_prefixes` helper instead.
2026-06-28 19:42:54 -04:00
aesoft 113ae1188a Merge pull request #2681 from guyoron1/fix/run-signal-exit-code
fix(run): propagate signal exit code instead of unwrap_or(1)
2026-06-28 18:15:13 +02:00
guy oron a58de9d60a fix(run): propagate signal exit code instead of unwrap_or(1)
`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
2026-06-28 09:54:57 +03:00
aesoft d3553fb7ee Merge pull request #2641 from rtk-ai/fix/grep-faithful-engine
refacto(search): run the invoked engine instead of substituting rg for grep
dev-0.43.0-rc.292
2026-06-26 20:00:02 +02:00
Adrien Eppling 05f3c54886 fix(grep): surface the engine error and exit code, add nothing
- 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)
2026-06-26 19:43:51 +02:00
Adrien Eppling 059131cd56 test(grep): rename compress suite to search_compress, cover binary skip
- 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
2026-06-26 17:44:57 +02:00
Adrien Eppling c66e510372 test(grep): cover #1436 comment-reported cases
- 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)
2026-06-26 17:34:26 +02:00
Adrien Eppling 37ee6cf6fa fix(grep): match real grep output and read piped stdin
- 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)
2026-06-26 17:25:01 +02:00
Ilia Alshanetsky 30fe7a4090 feat(php): consolidated PHP tooling (php, artisan, phpunit, phpstan, pest, paratest, ecs, pint)
Consolidates the PHP-tooling work from three upstream PRs plus a new
Pint module, leaving phpt to its own PR (#1503).

- rtk php / rtk artisan: syntax check (-l) and Laravel artisan wrapper.
- rtk phpunit: structured-state parser, aggregate counts, bounded
  failure list. Uses runner::run_filtered.
- rtk phpstan: typed serde::Deserialize parser for --error-format=json,
  groups errors by file, sorts by count desc. Utility commands
  (--version, list, clear-result-cache) pass through unchanged.
- rtk pest / rtk paratest: shared test_output helper.
- rtk ecs / rtk pint: code-style fixers; pint uses --format=json for
  structured per-file rule counts.

Composer custom-bin-dir detection: composer_bin_dirs() reads
COMPOSER_BIN_DIR and composer.json config.bin-dir, so tools/bin/phpunit
classifies identically to vendor/bin/phpunit. registry.rs normalizes
tool paths before matching.

Sources:
- rtk-ai/rtk#1246 (aaronflorey, self-closed): php, artisan, ecs, pest,
  paratest, test_output, utils, composer_bin_dirs, registry normalization.
- rtk-ai/rtk#874 (Beninho, open): phpunit state-machine parser.
- rtk-ai/rtk#1110 (LucianoVandi, open): phpstan typed parser.
- New: pint_cmd.rs.

Tests: discover::registry 253 pass; cmds::php 36 pass; cargo build
--release 0 errors; cargo fmt --check clean.
2026-06-26 10:04:40 -04:00
Adrien Eppling eafadcee00 fix(grep): run the invoked engine instead of substituting rg for grep
- 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
2026-06-26 14:11:28 +02:00
aesoft 9a52647f65 Merge pull request #2499 from hgunduzoglu/fix/git-worktree-list-exit-code
fix(git): propagate exit code on git worktree list failure
dev-0.43.0-rc.291
2026-06-26 13:58:31 +02:00
aesoft 29272484de Merge pull request #2496 from hgunduzoglu/fix/git-commit-false-success
fix(git): propagate exit code when commit fails instead of reporting ok
dev-0.43.0-rc.290
2026-06-26 13:24:34 +02:00
aesoft a9b2ef5b3c Update git.rs 2026-06-26 13:16:32 +02:00