Skip to content

feat(rewrite): translate PowerShell cmdlets to rtk subcommands - #3439

Open
make0uthill wants to merge 2 commits into
rtk-ai:developfrom
make0uthill:feat/powershell-cmdlet-rewrite
Open

feat(rewrite): translate PowerShell cmdlets to rtk subcommands#3439
make0uthill wants to merge 2 commits into
rtk-ai:developfrom
make0uthill:feat/powershell-cmdlet-rewrite

Conversation

@make0uthill

Copy link
Copy Markdown

Summary

The RULES table keys off the command word, so PowerShell cmdlets — which share no name with their GNU counterparts — never match it. On Windows the agent reaches for Get-ChildItem and Select-String rather than ls and grep, so those calls reach the model as unfiltered output and the hook saves nothing.

Translates the three cmdlets whose semantics map cleanly onto an rtk subcommand, plus their common aliases (gc, gci, dir, sls):

Get-Content foo.txt -TotalCount 5      -> rtk read foo.txt --max-lines 5
Get-ChildItem src -Recurse             -> rtk ls -R src
Select-String -Path a.txt -Pattern rtk -> rtk grep -i rtk a.txt

Two details worth flagging for review:

  • The -i on Select-String is not cosmetic: the cmdlet matches case-insensitively by default while grep does not, so omitting it would silently narrow the result set.
  • Argument splitting cannot reuse shell_split, which treats a backslash as an escape and would turn D:\Code\x into D:Codex. ps_split keeps backslashes literal and preserves quoting.

Deliberately narrow. A cmdlet carrying any parameter without a faithful rtk equivalent (-Filter, -Raw, -File, -ErrorAction) returns None and runs unrewritten — mistranslating a flag changes what the command means, which is worse than forgoing the saving. Cmdlets are also only rewritten in RewriteContext::Normal: as a pipeline stage a cmdlet hands .NET objects to the next stage, and rtk's text output would not survive that.

Known gap, left for a follow-up: Get-ChildItem -Recurse -Filter *.rs could map faithfully onto rtk find . -name '*.rs' (and -File/-Directory onto -type f/-type d). Happy to add it here instead if you'd prefer it in one PR.

Test plan

  • cargo fmt --all && cargo clippy --all-targets && cargo test — 2562 passed, 0 failed
  • 8 new tests covering the translations, the pass-through cases, Windows paths, and pipeline stages
  • Manual testing: verified end-to-end through rtk hook claude with a PowerShell tool payload; rewritten and passed-through cases both behave as the tests describe.

Targets develop as required.

The RULES table keys off the command word, so PowerShell cmdlets — which
share no name with their GNU counterparts — never match it. On Windows the
agent reaches for `Get-ChildItem` and `Select-String` rather than `ls` and
`grep`, so those calls reached the model as unfiltered output and the hook
saved nothing.

Translates the three cmdlets whose semantics map cleanly onto an rtk
subcommand, plus their common aliases (`gc`, `gci`, `dir`, `sls`):

    Get-Content foo.txt -TotalCount 5      -> rtk read foo.txt --max-lines 5
    Get-ChildItem src -Recurse             -> rtk ls -R src
    Select-String -Path a.txt -Pattern rtk -> rtk grep -i rtk a.txt

The `-i` on Select-String is not cosmetic: the cmdlet matches
case-insensitively by default while grep does not, so omitting it would
silently narrow the result set.

Deliberately narrow. A cmdlet carrying any parameter without a faithful rtk
equivalent (`-Filter`, `-Raw`, `-File`, `-ErrorAction`) returns None and runs
unrewritten — mistranslating a flag changes what the command means, which is
worse than forgoing the saving. Cmdlets are also only rewritten in
RewriteContext::Normal: as a pipeline stage a cmdlet hands .NET objects to
the next stage, and rtk's text output would not survive that.

Argument splitting cannot reuse shell_split, which treats a backslash as an
escape and would turn `D:\Code\x` into `D:Codex`. ps_split keeps backslashes
literal and preserves quoting so a rewrite re-emits paths exactly as written.

Adds 8 tests covering the translations, the pass-through cases, Windows
paths, and pipeline stages.
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Follow-up to the review note in this PR: a name filter or a type constraint
makes Get-ChildItem a search rather than a listing, and that maps faithfully
onto find.

    Get-ChildItem src -Recurse -Filter *.rs -> rtk find src -name '*.rs'
    Get-ChildItem -Recurse -File            -> rtk find . -type f
    Get-ChildItem -Recurse -Directory       -> rtk find . -type d

Without -Recurse the search is depth-limited with -maxdepth 1: Get-ChildItem
only descends when asked, while find recurses by default.

-Force is dropped rather than translated when routing to find — it makes
Get-ChildItem include hidden entries, which is already find's default.

Still passed through: -Include with a comma-separated list, which would need
`find ( -name a -o -name b )`, and -File combined with -Directory.
@make0uthill

Copy link
Copy Markdown
Author

Pushed the follow-up: -Filter/-Include/-File/-Directory now route to rtk find instead of passing through, so the known gap noted above is closed in this PR rather than a separate one.

Get-ChildItem src -Recurse -Filter *.rs -> rtk find src -name '"*.rs"'
Get-ChildItem -Recurse -File            -> rtk find . -type f

Without -Recurse the search is depth-limited with -maxdepth 1, since Get-ChildItem only descends when asked while find recurses by default. -Include with a comma-separated list still passes through.

Full suite green, 2571 passed.

@make0uthill

Copy link
Copy Markdown
Author

Correction to my note above: the suite is 2563 passed, 0 failed (I quoted 2563 as 2571 by mistake). Nothing else in that comment changes.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants