Skip to content

fix(ls): keep the child's order when a sort key is given - #4506

Open
houtinghang wants to merge 1 commit into
rtk-ai:developfrom
houtinghang:fix/ls-keep-sort-order
Open

houtinghang wants to merge 1 commit into
rtk-ai:developfrom
houtinghang:fix/ls-keep-sort-order

Conversation

@houtinghang

Copy link
Copy Markdown

Summary

rtk ls -t, -lt and -S list every directory before every file, so the first line is no longer the newest (or largest) entry (#4302). compact_ls always split the child's output into a dirs list and a files list and printed them in that order, whatever the user had asked ls to sort by. An agent that runs ls -t | head -1 to find the newest file gets a directory or an older file, and the Claude Code hook rewrites ls -lt into rtk ls -lt, so the agent never chose to run the rewritten command.

  • Remember whether each entry is a directory while reading the child's output, and only partition directories to the front when no sort key was given.
  • With -t, -S, -U, -X, -v, -f or GNU --sort=WORD (abbreviations resolved through the existing grammar, so --sor time counts), the child already ordered both kinds together and that order is kept as-is. On BSD only -t, -S and -f are sort keys (-U picks the timestamp -t sorts by; -v and -X mean something else), so the grouping stays there.
  • Plain -l, -a and -r behave exactly as before: directories first, then files.
  • The display cap now trims the tail of the sorted order instead of the tail of the files, so the newest entries survive truncation.
  • Not covered: -c / -u / --time=WORD without -l or -t, which GNU ls sorts by on its own. RTK always hands the child -l, which makes GNU sort by name again, so those need a different change (forwarding --sort=time) and are left alone here.

Tests: test_plan_sort_key_keeps_child_order pins which flags count as a sort key on GNU and BSD, test_compact_sorted_keeps_child_order / test_compact_unsorted_groups_dirs_first use the issue's ls -lt output with and without a sort key (short and long rendering), and test_compact_sorted_truncates_from_the_tail checks the cap keeps the head of the sorted order. The existing compact_ls tests pass sorted = false and are otherwise unchanged.

Closes #4302

Tests

  • cargo fmt --all --check
  • cargo clippy --all-targets (no warnings)
  • cargo test (full suite: 4001 unit tests pass). One integration test, unwritable_hooks_dir_never_breaks_the_hook, fails in my WSL environment with and without this change because the tests run as root there and root ignores the chmod the test relies on; unrelated to this PR.
  • End-to-end with the issue's reproducer (touch -t timestamps plus big.bin) through the built binary, develop vs. this branch:
$ ls -t                      $ rtk ls -t (develop)     $ rtk ls -t (this PR)
new.txt                      zdir/                     new.txt  0B
old.txt                      adir/                     old.txt  0B
big.bin                      new.txt  0B               big.bin  4.9K
zdir                         old.txt  0B               zdir/
adir                         big.bin  4.9K             adir/

$ ls -S                      $ rtk ls -S (develop)     $ rtk ls -S (this PR)
big.bin                      zdir/                     big.bin  4.9K
zdir                         adir/                     zdir/
adir                         big.bin  4.9K             adir/
new.txt                      new.txt  0B               new.txt  0B
old.txt                      old.txt  0B               old.txt  0B

rtk ls -lt matches the same order with the octal column; rtk ls, rtk ls -l and rtk ls -r print the same output as develop.

AI disclosure

Written with the help of Claude (Anthropic); I reviewed the change and the test results above before opening this PR.

🤖 Generated with Claude Code

`rtk ls` always regrouped the listing with directories first, even when
the user had asked `ls` for a particular order. `rtk ls -t`, `-lt` and
`-S` therefore printed every directory ahead of every file, so an agent
running `ls -t | head -1` to find the newest file got a directory, or an
older file, instead. The Claude Code hook rewrites `ls -lt` into
`rtk ls -lt`, so this changed the meaning of a command the agent never
chose to run.

Record which entries are directories while reading the child's output,
and only partition them to the front when no sort key was given. With
`-t`, `-S`, `-U`, `-X`, `-v`, `-f` or GNU `--sort=WORD` the child already
ordered both kinds together, and that order is the answer, so it is kept
as-is (BSD's `-U`, `-X` and `-v` are not sort keys and keep the grouping).
Plain `-l`, `-a` and `-r` still group directories first as before. The
display cap now also drops the tail of the sorted order rather than the
tail of the files, so the newest entries survive truncation.

Fixes rtk-ai#4302
@rtk-wshm-sync-bot rtk-wshm-sync-bot Bot added bug Something isn't working ls labels Oct 10, 2026
@rtk-wshm-sync-bot

Copy link
Copy Markdown

wshm · Automated triage by AI

📊 Automated PR Analysis

🐛 Type bug-fix
🟢 Risk low

Summary

Fixes rtk ls so that when a sort key like -t or -S is passed, entries keep the child ls process's sort order instead of always grouping directories before files. Adds a sorted flag threaded through LsPlan and compact_ls, detects sort-key flags across GNU/BSD flavors, and adjusts truncation to trim from the tail of the sorted order rather than the tail of the files list.

Review Checklist

  • Tests present
  • Breaking change
  • Docs updated

Linked issues: #4302


Analyzed automatically by wshm · This is an automated analysis, not a human review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ls

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rtk ls -t / -lt groups directories before files, breaking mtime order

1 participant