1503 Commits

Author SHA1 Message Date
Nicolas Le Cam 29f9bb7161 Merge pull request #3575 from rtk-ai/fix/git-log-grep-value-misdetected-as-patch-flag
fix(git): don't misdetect a value-taking option's argument as a patch  flag
dev-0.45.1-rc.362
2026-08-20 13:17:36 +02:00
Nicolas Le Cam f8d636dde1 fix(git): restore -- before requests_raw_log_output in run_log
Every other run_* function that inspects args for "--" (run_diff,
run_checkout) calls args_utils::restore_double_dash() first to
re-insert the "--" that clap's trailing_var_arg strips out. run_log
never did, so requests_raw_log_output()/log_arg_tokens()'s own
take_while(|arg| *arg != "--") check was dead code in production:
by the time run_log saw args, the literal "--" was already gone.

Concretely, `rtk git log -- -p` lost its "--" and -p (a file
literally named -p, a valid pathspec) was misdetected as the real
patch flag, routing to raw passthrough instead of RTK's filtered
log. Added an end-to-end regression test (real rtk binary, real git
repo) alongside the existing git_log_patch_output_matches_raw_git
test, since restore_double_dash reads live process args and can't
be exercised by calling run_log's internals directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 09:18:22 +02:00
Nicolas Le Cam 3034b3923f Merge pull request #3595 from ousamabenyounes/fix/benchmark-dir-wipes-tracked-harness
fix(cicd): stop benchmark.sh deleting the tracked scripts/benchmark harness
dev-0.45.1-rc.361
2026-08-20 02:24:21 +02:00
Nicolas Le Cam 983d0c956d Merge pull request #3266 from breisnerlopez/fix/tee-recovery-slug-collision
fix(tee): hash long recovery-file slugs to prevent collisions and shorten hints
2026-08-20 02:22:58 +02:00
Nicolas Le Cam ca8976730a fix(git): git log --stat/--numstat/etc. weren't requesting raw passthrough
requests_raw_log_output only recognized -p/-u/--patch variants as needing
the raw path. --stat, --numstat, --name-only, --name-status, --raw,
--shortstat, --dirstat, and --summary change git's raw output shape the
same way -p does, but weren't listed — RTK's injected --pretty=format +
---END--- markers can't coexist with them, so the diffstat/name-list block
got misparsed as the start of the next commit, mangling output silently
instead of just leaving it unfiltered.

Also share one log_arg_tokens() pass across run_log's flag/limit checks
instead of retokenizing per check, and drop a redundant "-n" match arm
already covered by consumes_next_token_as_value.
2026-08-20 01:00:23 +02:00
Nicolas Le Cam 9bbf55cd07 fix(git): value/limit/format detection for git log misdetects --grep values as flags
has_limit_flag, has_format_flag, wants_merges, and parse_user_limit scanned
args positionally with no awareness that a value belonging to --grep,
--author, etc. can itself look like a flag (-5, --pretty, --merges),
reproducing the same misdetection class this branch already fixed for -p.

Unify these into a single log_arg_tokens tokenizer shared by
requests_raw_log_output, real_flag_args, and parse_user_limit, which also
stops at the -- pathspec separator so a literal path like -5 after -- isn't
misread as a flag.
2026-08-20 00:39:18 +02:00
Nicolas Le Cam 84169e27da fix(git): add --diff-algorithm/--diff-filter to value-consuming options
Cross-checked git-log(1)'s full option list against
consumes_next_token_as_value() (using git log <opt> <token> -1 against
real git 2.53.0 to see whether <token> gets swallowed as the option's
value or leaks through as a positional arg). --diff-algorithm and
--diff-filter both take a required, separate-token value
(`git log --diff-algorithm -p` rejects -p as an invalid algorithm
rather than treating it as the patch flag) but were missing from the
list, so a genuine -p right after either was misdetected as the
option's own value and the raw-patch request went undetected.

Every other bracket/optional-value option checked (--format, --pretty,
--stat, -M, -C, -B, etc.) is correctly attached-value-only and stays
out of the list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 00:08:07 +02:00
Nicolas Le Cam 1a1b306e31 fix(git): --max-parents/--min-parents also only take an attached value
Audited every option in consumes_next_token_as_value() against real
git 2.53.0 behavior (git log <opt> <token> -1, checking whether the
token gets swallowed as the option's value or leaks through as a
positional arg). --max-parents and --min-parents behave like -U:
`git log --max-parents 2` fails with "ambiguous argument '2'", so a
real -p right after them was being misread as their value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 00:04:32 +02:00
Nicolas Le Cam 705a2f8a90 fix(git): -U, --unified, --expand-tabs don't take a separate-token value
These options only accept an attached value (-U3, --unified=3,
--expand-tabs=4) — verified against git 2.53.0, where
`git log --expand-tabs 4` fails with "fatal: ambiguous argument '4'"
instead of treating 4 as the value. consumes_next_token_as_value()
was swallowing the next token for them anyway, so a real -p right
after one of these was misread as their value and the raw patch
request went undetected.

Addresses review feedback from @aeppling on #3575.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 00:01:41 +02:00
breisnerlopez 9ee1532535 refactor(tee): simplify short_hash and correct its collision framing
Address review nits on #3266:
- Collapse short_hash to format!("{:x}", Sha256::digest(..))[..6] per suggestion.
- Reword docs: 6 hex = 24 bits is not collision-resistant on its own (birthday
  bound ~few thousand slugs); safety here comes from also requiring the same
  readable prefix and epoch second.
2026-08-18 01:10:39 +02:00
Ousama Ben Younes 962554d457 fix(cicd): stop benchmark.sh deleting the tracked scripts/benchmark harness
`BENCH_DIR="$(pwd)/scripts/benchmark"` is a tracked directory: besides the
gitignored `unix/`, `rtk/` and `diff/` output dirs it holds the TypeScript
VM-benchmark harness (`run.ts`, `cleanup.ts`, `rebuild.ts`, `lib/*.ts`,
`cloud-init.yaml`). `rm -rf "$BENCH_DIR"` therefore wiped all 7 tracked files
from the working tree on every local run (`$CI` unset), which is exactly when
a contributor runs the benchmark before pushing.

Wipe only the three gitignored output subdirectories instead. Stale output is
still cleared between runs; the harness survives.

Follow-up to #3430 (rtk-ai/rtk#3430 review).
2026-08-17 15:06:04 +00:00
Nicolas Le Cam ba7a9ce0d9 Merge pull request #3430 from ousamabenyounes/fix/benchmark-negative-curl-cargo
fix(benchmark): avoid negative curl/cargo cases that fail the benchmark job
dev-0.45.1-rc.359
2026-08-17 03:03:41 +02:00
Ousama Ben Younes d003920601 refactor(cargo): extract compute_test_summary and cover the never-worse guard
Review follow-up on #3430:

- Replace the IIFE closure in `CargoTestHandler::format_summary` with a private
  `compute_test_summary` on the existing `impl CargoTestHandler`, and apply
  `never_worse` at the call site, as suggested.
- Add `test_cargo_test_summary_uses_raw_when_summary_is_larger`: a one-line
  compile failure whose "cargo test: N errors, ..." header is larger than the
  raw output, so the guard must return the raw output.

RED→GREEN: the new test fails on upstream develop (returns the 105-byte header
form instead of the 62-byte raw), passes here. Full suite: 2612 unit + 78
integration tests pass; benchmark 0 negative (develop baseline: 1).
2026-08-17 00:31:16 +00:00
Ousama Ben Younes 7f6156f3a7 fix(cicd): make benchmark loopback server port-agnostic and cleanup set -e safe
Review follow-up on #3430:

- Bind `python3 -m http.server 0` so the kernel picks a free port and read the
  chosen one from the (unbuffered) server log, instead of hardcoding 8899 which
  fails needlessly when that port is already in use.
- Make `cleanup_net_fixtures` failure-tolerant: it runs from an EXIT trap under
  `set -e`, so `[ -n "$PID" ] && kill ...` aborted the whole handler whenever
  `kill` failed (server already dead), leaking the fixture dir and downloads.
- Give the wget case an explicit skip line instead of silently disappearing.
  wget rejects `file://` ("Unsupported scheme"), so there is no offline URL to
  fall back to when the loopback server is unavailable.

RED (PR HEAD): fixture dir NOT removed when kill fails; server unusable with
8899 taken. GREEN: fixture dir removed; server up on an ephemeral port.
2026-08-17 00:31:16 +00:00
Ousama Ben Younes 13bd8f216d fix(cargo): apply never-worse guard to test summary
CargoTestHandler.format_summary could emit a compacted summary larger than
the raw output on tiny runs, same failure mode CargoBuildHandler already
guards. Wrap all return paths with never_worse so the raw output wins when
the summary would be bigger.
2026-08-16 23:55:25 +00:00
Ousama Ben Younes 4644fa8fa4 fix(benchmark): serve curl/wget fixtures from loopback, drop mockhttp.org
The remaining online calls (curl robots.txt + wget /json on mockhttp.org)
were both a network dependency and non-deterministic. Serve fixed local
fixtures over a loopback http.server so curl and wget get real
Content-Type headers (exercising JSON minification), fully offline. curl
falls back to file:// when python3 is unavailable. Clean up the server,
temp fixtures, and the ./data.json download on exit.
2026-08-16 23:55:25 +00:00
Ousama Ben Younes 497ef8bce2 test(cargo): share tiny build output fixture 2026-08-16 23:55:25 +00:00
Ousama Ben Younes 4947edaf0e fix(benchmark): avoid negative cargo and curl cases 2026-08-16 23:55:25 +00:00
Nicolas Le Cam 32c83cc60a Merge pull request #3147 from guyoron1/fix/rewrite-test-env-sensitivity
fix(test): accept both Ask and Allow verdicts in rewrite tests
dev-0.45.1-rc.358
2026-08-17 00:34:29 +02:00
Nicolas Le Cam 48638d6a98 refactor(test): drop eval helper, regroup verdict-mapping tests
Inline evaluate_with_verdict() calls directly in unattestable_passthrough
tests instead of through a locally-named eval() alias, and move the two
verdict-to-outcome tests (allow/deny) up into the parent tests module since
they aren't about unattestable-construct passthrough.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 00:26:43 +02:00
Nicolas Le Cam 57872f8d7e Merge pull request #2717 from guyoron1/fix/windows-gbk-encoding
fix(core): decode process output using Windows console code page
dev-0.45.1-rc.357
2026-08-17 00:08:08 +02:00
guyoron1 668f449faf fix(test): insulate rewrite tests from the machine's permission settings
The unattestable_passthrough tests called evaluate(), which calls
check_command() and reads the developer's Claude Code settings files
(.claude/settings.local.json, ~/.claude/settings.json). A local
`Bash(git *)` allow rule turned the expected Ask into Allow, so the two
rewrite assertions failed on that machine and passed everywhere else;
deny rules would likewise have broken the four Passthrough assertions.

Give evaluate() the same verdict-injection seam that permissions.rs
already exposes via check_command_with_rules: evaluate_with_verdict()
holds the decision logic and takes the verdict as a parameter, while
evaluate() stays the thin wrapper that looks it up. The tests pin
PermissionVerdict::Default, so they read no settings at all and assert
the exact outcome again rather than accepting either verdict.

Added coverage for the Allow and Deny verdicts so the mapping stays
tested in both directions without depending on host configuration.

Verified by running the test binary with HOME pointed at settings files
containing deny, ask, allow, and no rules: 8/8 pass in all four, and
with the repo's own settings.local.json in place.

Closes #3146
2026-08-16 13:34:08 +03:00
guyoron1 32dc612c2e fix(core): keep the signal diagnostic when capturing child output
Moving call sites onto exec_capture swapped exit_code_from_output for
status_to_exit_code, which returns the same 128 + signal but drops the
stderr line explaining that the child was killed rather than exiting.
A command that dies to an OOM kill would just report 137 with no reason.

Route both capture helpers through one function that calls
exit_code_from_output, using the program name as the label so no call
site has to pass one. Covered by a test that terminates a child with
SIGTERM and asserts the 128 + signal code survives.
2026-08-16 12:05:25 +03:00
guyoron1 f496f59b77 fix(core): decode per line, cover OEM code pages, and centralize on exec_capture
Addresses the inline review on #2717.

decode_process_output
- Decode a line at a time instead of reinterpreting the whole buffer at
  the first bad byte. Valid UTF-8 lines keep their bytes; only lines that
  fail UTF-8 validation go through the code page, so one stray byte no
  longer mangles output that was almost entirely UTF-8. The line is the
  unit because a byte run is not one: GB18030's four-byte sequences embed
  bytes in the ASCII digit range, so any rule that ends a run below 0x80
  splits them. \n cannot appear as a trail byte in any encoding handled
  here, and a process does not switch encoding mid-line.
- A code page result is only accepted when it decodes cleanly, so a UTF-8
  line with a corrupt byte falls back to lossy UTF-8 rather than mojibake.
- Replace the hand-written code page table with the codepage crate, as
  suggested. That also fixes 54936, which was mapped to GBK and now
  correctly resolves to gb18030.
- Add oem_cp for the legacy OEM/DOS pages (437, 850, 852, …) that plain
  cmd.exe still defaults to in many locales. encoding_rs implements only
  WHATWG encodings, so codepage alone returns None for them.
- Fall back to GetACP when GetConsoleOutputCP reports no console, which
  is the piped case rtk normally runs in, and warn once instead of
  falling back to lossy silently.
- Cache the code page lookup in a OnceLock.
- The mapping and the walk take the code page as a parameter, so they are
  compiled and unit-tested on every platform rather than only Windows.

Call sites
- Route the remaining production sites through stream::exec_capture and
  exec_capture_stdin rather than decoding at each one, so future callers
  inherit decoding. git commit keeps inherited stdin via the _stdin
  variant. Test-only sites go back to from_utf8_lossy: they assert on
  rtk's own UTF-8 output, where a console code page has no meaning.
- Decode the streamed path (read_lines_lossy) too — the OEM/ANSI lines
  its comment describes were still going straight to U+FFFD.
- curl keeps its body on from_utf8_lossy: a response body is a network
  payload whose encoding comes from the HTTP charset, not the local
  console, and non-UTF-8 bodies already take the binary passthrough for
  #1087. Only curl's own stderr is code page decoded.

git commit summary parsing
- parse_commit_output sliced from byte 1, which panics when the first
  line starts with a multi-byte character — git prints hook output before
  its summary, and a lossily decoded line starts with a multi-byte
  U+FFFD. Locate the bracket pair with find instead, so both indices are
  character boundaries.

Verified: unit tests for the walk, GBK, gb18030, CP437/850, mixed lines,
truncated input and every byte value; a test pinning that output without
a code page stays byte-identical to from_utf8_lossy; and the Windows-only
lookup cross-compiled for x86_64-pc-windows-msvc.
2026-08-15 08:20:24 +03:00
Nicolas Le Cam 3cc80b2433 fix(git): don't misdetect a value-taking option's argument as a patch flag
requests_raw_log_output() matched bare -p/-u/--patch tokens anywhere in
the args, without checking whether the previous token was an option like
--grep or -S that consumes the next token as its value. `git log --grep
-p` searches commit messages for the literal string "-p" (verified
against real git 2.53.0: no diff output, identical to --grep=-p) but RTK
treated it as a patch request and skipped its own filtering/limit,
dumping raw uncapped git log output for what is actually a plain grep
search.

Skip the value token after any known value-taking git log/diff option
before checking for the patch flags.
2026-08-15 04:01:34 +02:00
Nicolas Le Cam d977e1c316 Merge pull request #2951 from kingpy-bot/fix/git-log-patch-passthrough-2944
fix(git): preserve patch output from log commands
dev-0.45.1-rc.356
2026-08-15 01:49:56 +02:00
Nicolas Le Cam 40e4f3aac9 fix(git): respect -- pathspec separator in git log patch detection
requests_raw_log_output() scanned the full args slice for patch flags
like -p, so `git log -- -p` (a literal pathspec named -p) was
misdetected as a raw patch request and skipped RTK's normal filtering.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 01:40:30 +02:00
guyoron1 b35ff3a374 fix(core): route all child-process output decoding through decode_process_output
Review feedback (KuSh): the helper existed but most filter modules still
called String::from_utf8_lossy directly, so non-UTF-8 console output
(e.g. GBK on Chinese-locale Windows) was still mangled for most commands.

Wire decode_process_output through the remaining child-output call sites:
git, go, aws, curl (non-binary paths only — raw binary passthrough for
#1087 is untouched), system read, and discover registry. File-content
decoding (dotnet TRX XML, hook trust snippets) intentionally keeps
from_utf8_lossy since console code pages don't apply there.
2026-08-14 22:00:32 +03:00
guy oron de38f4493d style: expand unsafe block to satisfy rustfmt 2026-08-14 21:57:29 +03:00
guy oron 945c3a57b3 fix(core): use windows-sys crate for code page detection, fix CI test
Replace hand-written FFI extern block with windows-sys crate binding
for GetConsoleOutputCP to satisfy semgrep unsafe-block rule. Rewrite
test_decode_process_output_gbk to test encoding logic directly via
codepage_to_encoding(936) instead of relying on the CI runner's
actual console code page (which is not GBK on GitHub Actions).
2026-08-14 21:57:29 +03:00
guy oron 5bd410eb51 fix(core): decode process output using Windows console code page
On Windows with non-UTF-8 console code pages (e.g., GBK for Chinese
locale), child process output is mis-decoded by String::from_utf8_lossy,
producing mojibake. Add decode_process_output() that detects the console
output code page via GetConsoleOutputCP() and decodes with encoding_rs.

Replaces from_utf8_lossy in the core capture paths (exec_capture,
exec_capture_stdin, TOML filter path, proxy streaming path). Module-
specific call sites left for follow-up.

Fixes #2452
2026-08-14 21:57:29 +03:00
Nicolas Le Cam 6f0b0cad29 Merge pull request #2952 from zbl1998-sdjn/fix/discover-windows-drive-colon
fix(discover): sanitize drive-letter colon so Windows discover finds sessions
dev-0.45.1-rc.355
2026-08-13 02:07:26 +02:00
Nicolas Le Cam 40fccbd8ef test(discover): remove duplicated test 2026-08-13 01:58:54 +02:00
Nicolas Le Cam 1989899664 Merge pull request #2997 from albatrossflyon-coder/fix/utf8-line-drop-in-stream-filters
fix(stream): decode lossily instead of dropping lines on invalid UTF-8
dev-0.45.1-rc.354
2026-08-12 02:04:12 +02:00
Chris Brown ae5d1aec0c fix(stream): address review feedback on read_lines_lossy
KuSh's review on this PR flagged two real issues in read_lines_lossy:
- Ok(0) | Err(_) => None treated a genuine I/O error the same as clean
  EOF, silently truncating output on a real read failure -- exactly
  the "failing build looks clean" class of bug this PR exists to fix,
  just relocated rather than removed.
- A fresh Vec was allocated every iteration on a hot streaming path
  instead of reusing std's own buffered split.

Replaced the hand-rolled read_until loop with BufReader::split(b'\n'),
per the reviewer's suggested implementation: reuses std instead of
reimplementing it (except CR-stripping, which split() doesn't do),
and now surfaces a genuine I/O error to stderr instead of silently
conflating it with EOF.

Added a test with a Read impl that yields good lines then a real
error, confirming the error path doesn't panic or hang and the
already-read lines are still preserved.
2026-08-11 18:33:00 -05:00
aesoft 9936b2b9ce Merge pull request #3449 from rtk-ai/feat/copilot-hook-self-heal
fix(hooks): copilot self heal dual hooks (drop camelCase entry)
2026-08-06 21:33:35 +02:00
aesoft de1f568c50 Merge pull request #3391 from xavierpestel-ai/feat/vibe-hook-support
feat(hooks): transparent pre_tool rewrite for Mistral Vibe CLI (closes #800)
dev-0.45.0-rc.351
2026-08-06 17:27:11 +02:00
Xavier Pestel 9dae4d7036 refactor(vibe): address second-round nits from @aeppling
Two follow-ups to https://github.com/rtk-ai/rtk/pull/3391#pullrequestreview-4865940066:

1. Move the summary-verb mapping onto VibeHookPatchOutcome as
   summary_verb() -> Option<&'static str>, returning None for Skipped.
   The call site becomes 'else if let Some(v) = outcome.summary_verb()'
   which collapses the guard and the match into a single decision point
   and removes the unreachable!() branch. If a future variant is added,
   the compiler forces a decision in summary_verb() and the caller
   handles it naturally through the Option.

2. uninstall_vibe now prints a stderr warning when resolve_vibe_dir()
   fails instead of silently returning Ok(()). Users asking to uninstall
   no longer see an empty response when the home dir can't be resolved.
   uninstall_gemini has the same swallow-and-return-Ok pattern; leaving
   that untouched here to keep the diff scoped to Vibe, but the same
   improvement would apply as a follow-up.

Both are quality improvements with no behavior change on the happy path.
2026-08-05 17:33:45 +02:00
Xavier Pestel 4ff41bd12b ci(semgrep): suppress filesystem-deletion warnings on Vibe uninstall paths
The Semgrep security scan on PR #3391 flagged 2 new fs::remove_file calls
in uninstall_vibe_at as blocking findings under the filesystem-deletion
rule (WARNING severity, but the CI runs semgrep --error which promotes
all findings). Both calls are legitimate uninstall behavior:

- prompt file removal at src/hooks/init.rs:4697 — removes only
  ~/.vibe/prompts/rtk.md, which RTK installed itself.
- hooks.toml removal at src/hooks/init.rs:4714 — removes the file only
  when it becomes empty after stripping the RTK entry, so no orphan
  empty file is left behind.

Both suppressions follow the existing repo convention (`// nosemgrep:
<rule-id> -- <justification>` on the line above the code), matching
precedents in src/discover/lexer.rs and src/core/stream.rs.
2026-08-05 17:10:26 +02:00
Adrien Eppling 99841dc40d chore(hooks): suppress semgrep on temp-file cleanup 2026-08-05 17:03:35 +02:00
Adrien Eppling 157b0acc98 refacto(copilot): rm migration tests 2026-08-05 16:31:51 +02:00
Adrien Eppling f676ad7537 test(hooks): end-to-end matrix for Copilot self-heal safety
Real-binary tests: heal correctness, refusal matrix, response integrity, concurrency, unwritable dir.
2026-08-05 16:24:18 +02:00
Adrien Eppling db31da9af4 fix(hooks): heal only rtk's own legacy camelCase entry, keep user config
Match the exact stock camelCase entry instead of the whole file, so extended configs (extra hooks/keys) heal too; anything non-stock stays untouched.
2026-08-05 16:09:31 +02:00
Adrien Eppling d1f71398fd fix(hooks): self-heal stale dual-schema Copilot hook config
Legacy camelCase invocation rewrites the old stock config to the single-schema form, so upgrades need no manual rtk init --copilot.
2026-08-05 15:57:03 +02:00
Xavier Pestel 0430df48ab refactor(vibe): drop defensive-only comment on Host::Vibe arm
The comment was previously added in response to review point #5. Removed
per follow-up feedback — the arm itself is self-explanatory in context
alongside the other Host variants.
2026-08-05 15:38:06 +02:00
Xavier Pestel 94ae76b2da docs(vibe): add hooks/vibe/README.md and link from Directory Structure
Every other agent with a dedicated hook implementation carries a
hooks/<agent>/README.md (see antigravity/cline/opencode/copilot/hermes
for the shape). The initial Vibe commit skipped this, leaving Vibe as
the odd one out in the hooks/ layout.

- Add hooks/vibe/README.md following the Copilot template (Rust binary
  hook, no shell dependency). Documents the pre_tool hook location,
  input JSON shape, rewrite response, passthrough / deny behavior,
  and the belt-and-suspenders prompt fallback.
- Fix hooks/README.md Directory Structure entry to point at
  vibe/README.md (previously claimed 'no dedicated subdirectory').
2026-08-05 15:37:02 +02:00
Xavier Pestel 1847b07f7a fix(vibe): address PR review — exit code contract, tests, telemetry, docs
Addresses @aeppling's review on #3391:

Blocking fixes:
- run_vibe now returns Ok(()) on malformed JSON (matches run_droid /
  run_copilot / run_cursor pattern). Prior code violated the exit-code
  contract documented at src/hooks/README.md:100 — a bad payload exited
  non-zero and blocked the agent's command. Fixed via a match on
  serde_json::from_str with a stderr warning fallback.
- Extract run_vibe_inner(input: &str) -> Option<String> from run_vibe so
  the hook contract is unit-testable (mirrors run_droid_inner). Public
  run_vibe becomes a thin stdin/stdout wrapper.
- Add 6 runtime tests exercising the hook contract: bash rewrite happy
  path, non-bash tool passthrough, empty command passthrough, malformed
  JSON returns None, unknown binary passthrough, substitution defers.

Should-fix:
- Telemetry agent detection: add ~/.vibe/hooks.toml to detect_hook_type()
  checks in src/core/telemetry.rs, plus the two test enum arrays so Vibe
  sessions no longer report as 'unknown' in rtk gain history.
- Dead deny arm: add a comment on Host::Vibe in permissions.rs
  documenting that the empty-rules branch is defensive scaffolding for
  when Vibe ships native denylist/allowlist config we can honor.
- Broken link: patch_vibe_hooks_toml skip-message now points at
  https://www.rtk-ai.app/guide/getting-started/supported-agents#mistral-vibe
  instead of a fragment that doesn't resolve.

Nits addressed:
- Install summary no longer prints 'hook installed' when the user chose
  PatchMode::Skip or declined the interactive prompt. patch_vibe_hooks_toml
  now returns a VibeHookPatchOutcome enum (Installed / AlreadyPresent /
  Skipped) and the caller gates the summary on it.
- Document the string-spacing tradeoff on vibe_hooks_toml_has_rtk: a
  reformatted 'name="rtk-rewrite"' would defeat idempotency, acceptable
  because we control the writer and toml_edit round-trip would clobber
  user comments.
- Fix stale line in src/hooks/README.md 'Adding New Functionality':
  hook_check.rs::maybe_warn() only checks the Claude Code hook now,
  not every agent.

Documentation:
- docs/guide/getting-started/supported-agents.md: frontmatter now lists
  Mistral Vibe, drop 'planned' from the intro, tier table row flipped
  from 'Planned (#800)' to 'Rust binary (pre_tool) / Yes', replace the
  ### Mistral Vibe (planned) placeholder with a full user-facing section
  modeled on Factory Droid (install/uninstall commands, hook mechanism,
  permission semantics, idempotency contract).
- hooks/README.md: agent count 9 -> 10, add Vibe entry to Directory
  Structure list, add Vibe row to Supported Agents table, add
  '### Mistral Vibe (Rust Binary)' entry to the JSON Formats section
  showing the pre_tool input shape and rewrite response shape.
- src/hooks/README.md: agent count 5 -> 6, add Vibe row to per-host
  ask-support table.
- README.md: '15 AI coding tools' -> '16'.

No behavior change for existing agents.
2026-08-05 13:55:09 +02:00
Xavier Pestel d480f1ec48 feat(hooks): add transparent hook support for Mistral Vibe CLI
Add `rtk init -g --agent vibe` and `rtk hook vibe` to route bash tool
calls through the RTK proxy via Vibe's newly-shipped pre_tool hook.

Implementation follows the Gemini / Droid pattern:
- Native binary hook (`rtk hook vibe`), no shell script dependency.
- Global-only install (`~/.vibe/hooks.toml`); user-scope only.
- Idempotent install: detects existing `name = "rtk-rewrite"` entry.
- Uninstall is surgical: strips only the RTK `[[hooks]]` block and the
  `~/.vibe/prompts/rtk.md` prompt file, preserving any other user hooks
  byte-for-byte. Removes hooks.toml only when it becomes empty.
- Hook response uses Vibe's documented `hook_specific_output.tool_input`
  rewrite contract with a `system_message` for UI visibility.

Vibe hook API reference:
https://docs.mistral.ai/vibe/code/cli/hooks

Closes #800.
2026-08-04 16:51:24 +02:00
aesoft 3044911b50 Merge pull request #3319 from rtk-ai/feat/rewrite-multiline-blocks
feat(rewrite): rewrite multiline blocks
dev-0.45.0-rc.350
2026-08-03 10:41:04 +02:00
Nicolas Le Cam 8722378a22 Merge pull request #3212 from rtk-ai/fix/copilot-parity-omit-permission-decision
fix(hooks): stop Copilot from silently deciding permission on unconfigured commands
dev-0.44.3-rc.349
2026-08-02 19:55:12 +02:00