The compact `git status` path ran `git status --porcelain -b -uall`. The
`-uall` flag expands fully-untracked directories into every file, while raw
`git status` collapses them (e.g. `node_modules/`). This made rtk output
larger than raw — measured ~29x on a 200-file untracked dir (5500B vs 191B) —
violating RTK's compress-or-match-raw invariant and inflating tokens.
Remove `-uall` so untracked directories collapse exactly like raw. This keeps
#991's actual fix intact: all modified/staged/renamed/conflict paths are still
shown with no grouped summaries or `... +N more` overflow markers (`-uall`
never affected those lines). Untracked files in partially-tracked dirs and any
paths git itself expands are still preserved by the formatter.
Measured (rtk vs raw git status):
- node_modules/ (200 files): 5500B (-2780%) -> 25B (+87% savings)
- normal (8 mod + 2 untracked): +71% -> +76%
- 17 modified (#991 case): unchanged at +58%, all 17 shown, 0 overflow markers
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When user runs 'git log -1 --format='%H' HEAD' where HEAD is a merge
commit, rtk was adding --no-merges which filtered out the merge commit
itself and returned the second parent instead. This made 'git log -1'
return wrong SHAs for merge commits.
Fix: don't add --no-merges when user explicitly passes -n N or
--max-count=N. When a user specifies an exact count they expect exactly
that many commits, not filtered results. Also skip --no-merges if user
already passed --merges or --no-merges explicitly.
Fixesrtk-ai/rtk#2009.
- Avoid underflow + 0 results caused by overwrite of global constant CAPS
- Soon user will be able to apply global configuration, this will change global CAPS and should not cause any underflow or 0 results, if overwrite sub >= CAPS -> use CAPS.
Address review feedback on #1976:
- Extract write_rtk_block() shared dispatcher: eliminates the duplicated
4-arm RtkBlockUpsert match between run_claude_md_mode and the former
upsert_copilot_instructions (now inlined). Both callers stay under the
~60-line guideline.
- Unify malformed handling: both paths now bail!() with a diagnostic and
the exact recovery command. CLAUDE.md previously warned and exited 0,
silently skipping the OpenCode plugin step; behaviour is now consistent.
- Reorder run_copilot_at: upsert copilot-instructions.md BEFORE writing
the hook config so a malformed file aborts the install without leaving
a stale .github/hooks/rtk-rewrite.json on disk.
Regression coverage:
- test_claude_md_mode_refuses_malformed_block mirrors the existing
copilot malformed test against the shared dispatcher contract.
- test_copilot_init_malformed_leaves_no_hook_on_disk pins the new write
order so a future re-order regression is caught.
cargo fmt / clippy --all-targets / test --bin rtk: clean (1909 passed).
The README badge and footer claimed MIT, but the LICENSE file is
Apache License 2.0. Updates both references so the README matches
the authoritative LICENSE file.
Closes#1996
Co-Authored-By: Claude <noreply@anthropic.com>
run_copilot(ctx) is now a thin wrapper around run_copilot_at(base, ctx);
tests pass a TempDir path so they no longer mutate process-global cwd.
This eliminates flakes when cargo test runs in parallel (the default).
The public API is unchanged; only the internal seam moves.
Refs #1964
Adds five additional tests around copilot-instructions.md upsert:
- idempotent re-init (no duplicated blocks, identical content)
- stale RTK block in-place update with surrounding user content preserved
- dry-run never creates or modifies the file
- fresh install creates file with RTK marker block when no file exists
- malformed pre-existing block (opening marker without closing) is
refused with a clear error rather than silently rewritten
Refs #1964
run_copilot() previously called write_if_changed() which truncated any
pre-existing .github/copilot-instructions.md, destroying user-authored
Copilot rules.
This routes copilot-instructions.md through upsert_rtk_block() (the
same idempotent marker-block helper already used for CLAUDE.md and
AGENTS.md). The COPILOT_INSTRUCTIONS constant is wrapped in
<!-- rtk-instructions v2 --> ... <!-- /rtk-instructions --> markers
so subsequent inits replace only RTK-owned content; the rest of the
file is preserved verbatim. Malformed pre-existing blocks (opening
marker without closing) are refused with a remediation message
rather than silently rewritten.
Fixes#1964, #1891
Adds a TDD red test that asserts pre-existing user content in
.github/copilot-instructions.md must survive 'rtk init --copilot'.
Currently fails because run_copilot() uses write_if_changed() which
truncates the file.
Refs #1964
Replace all bare magic offset literals (e.g. 21 for cap=20) with named
constants (MAX_XXX + 1) so offsets stay in sync if caps change.
Fix compose_ps to tee pre-formatted lines instead of raw tab-separated
input, so tail -n +N gives the agent readable content.
All 13 affected modules updated: container, dotnet, gh, glab, go, lint,
pnpm, pytest, rubocop, cargo, aws. 1884 tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- docker ps reverts to plain (no -a augmentation)
- docker ps -a / compose ps -a wired to the running/stopped split filter
- ruff Violations: capped at 50 + force_tee_tail_hint recovery
- tests for the ruff and pytest xfail caps
- fix signal truncation gaps + kush reviews
- added a new tee and hints function to give an hint with tail (avoid re-ingest of the head)
- extracted patterns in constants
## Problem (#963)
`rtk git push` reportedly times out: users see
`bash tool terminated command after exceeding timeout 30000 ms` while
plain `git push` to the same remote completes fine. P1-critical because
every Claude Code git push goes through rtk.
## Root cause
`run_push` used `cmd.stdin(Stdio::inherit()).output()`. `Command::output()`
captures both stdout and stderr until the child exits. Git push prints
its progress (`Counting objects` / `Compressing objects` / `Writing
objects`) to stderr and may prompt for SSH passphrases or HTTPS
credentials. With stderr captured, Claude Code's bash tool saw zero
output for 30+ seconds and killed the command — exactly the 30000 ms
message in the issue.
## Fix
Rewrite `run_push` on top of the streaming infrastructure that already
exists for this exact purpose (`stream::run_streaming` +
`FilterMode::Streaming`, added in 0.37.0).
Add a counterpart to `BlockStreamFilter<H: BlockHandler>` in
`src/core/stream.rs`: `LineStreamFilter<H: LineHandler>`. Where
`BlockStreamFilter` defaults to DROP and emits only collected blocks,
`LineStreamFilter` defaults to KEEP and lets handlers opt into dropping
noise. Trait surface mirrors `BlockHandler`:
- `should_skip(&mut self, line: &str) -> bool` — default false
- `observe_line(&mut self, line: &str)` — default no-op
- `format_summary(&self, exit_code, raw) -> Option<String>`
This lets future streaming commands reuse the line-oriented pattern.
`GitPushLineHandler` then becomes a tiny `LineHandler` impl:
- `should_skip` drops the high-volume progress phases (Enumerating /
Counting / Compressing / Writing objects, Delta compression, Total)
and blank lines.
- `observe_line` captures the up-to-date sentinel and the first ref
update target (e.g. `master`) for the summary.
- `format_summary` emits `ok <ref>` / `ok (up-to-date)` / `ok` on
success; nothing on failure (raw error lines already flowed through).
Stdin is inherited (`StdinMode::Inherit`) so SSH passphrase and HTTPS
credential prompts still reach the user. Tracking now records the real
raw output and the filtered output.
## Test plan
- [x] `cargo fmt --all -- --check`
- [x] `cargo clippy --all-targets -- -D warnings` — clean
- [x] `cargo test --all` — 1880 passed, 0 failed, 6 ignored
- Six unit tests cover the push handler: progress-prefix drop,
up-to-date summary, remote message passthrough, no-summary-on-failure,
first-ref-wins, and token-savings (>=60% on a representative payload).
- Four unit tests cover the new `LineStreamFilter` trait:
default-keep-all, skip-drops-matching, summary-propagates-exit-code,
observe-only-called-for-kept-lines.
## Notes
- Behaviour change: users now see git's native output line-by-line
(with progress phases stripped) plus a final `ok <ref>` summary,
instead of just the compact summary. This matches plain `git push`
more closely and is what the issue reporter expects.
- No regression for other filters: `run_pull`, `run_fetch`, `run_clone`
are untouched; only `run_push` is modified.
Closes#963🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
_Vibe Coded by Ousama Ben Younes_
_Developed With Ora Studio (Claude Code)_
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Several filters dropped or rewrote information an agent needs, producing
output that was misleading rather than merely compressed. This fixes the
clear-cut cases surfaced by the TheDecipherist/rtk-test suite:
- ls: stop hiding the `.env` file (removed `.env`/`env` from NOISE_DIRS).
- git status: restore the explicit "HEAD detached at <sha>" line instead of
the opaque "* HEAD (no branch)".
- log/docker logs: recognise CRITICAL/FATAL/ALERT/EMERGENCY/SEVERE (and
NOTICE) as severities so those lines are no longer silently filtered;
expose `log` as a `rtk pipe` filter and accept a positional filter name.
- wc: forward stdin to the child so `cat file | rtk wc` counts the pipe
instead of reporting 0 (new RunOptions::inherit_stdin).
- jest/vitest: include skipped/pending test count in the compact summary.
- pytest: surface xfailed/xpassed counts and list XFAIL/XPASS entries with
their reasons (adds `-rxX`).
- ruff/eslint: list individual violations with file:line:col, not just
rule/file group counts.
- pnpm/npm list: render the actual package list instead of the false-positive
"All packages up-to-date" when there's no upgrade info.
- git add: stay silent on a no-op instead of printing an ambiguous "ok".
- git stash: pass git's own message through instead of collapsing to "ok".
- docker ps: keep the Status column (health/restart info) and list
stopped/exited containers; docker images: show all images with full names;
docker compose ps: use `-a` so crashed services stay visible.
All 1870 unit tests pass; cargo fmt + clippy clean.
Previously `rtk docker compose logs --tail=20 web` would fall through to
the passthrough handler because clap had no `--tail` field on the `Logs`
subcommand, resulting in 0% token savings. Additionally, `run_compose_logs`
always fetched 100 lines regardless of user input.
- Add `#[arg(long, default_value_t = 100)] tail: u32` to `ComposeCommands::Logs`
- Thread the value through to the docker invocation
- Default unchanged (100 lines), so bare `rtk docker compose logs` behaves as before
Ports the core fix from #580, retargeted onto current develop layout
(`src/cmds/cloud/container.rs`). Relates to #578.
Co-authored-by: Mihir Dash <137862945+slice-mihird@users.noreply.github.com>