Skip to content

feat: integrate rtk for reducing LLM token consumption on agent shell commands#13615

Open
vaayne wants to merge 3 commits intomainfrom
feat/rtk-integration
Open

feat: integrate rtk for reducing LLM token consumption on agent shell commands#13615
vaayne wants to merge 3 commits intomainfrom
feat/rtk-integration

Conversation

@vaayne
Copy link
Collaborator

@vaayne vaayne commented Mar 19, 2026

What this PR does

Before this PR:
Agent Bash tool calls output raw, verbose shell command results that consume excessive LLM tokens.

After this PR:
Bash commands are transparently rewritten via rtk to produce concise, LLM-friendly output — reducing token consumption by 60-90% on common shell commands (cat, grep, find, ls, etc.).

image

Fixes #13600

Why we need it and why it was done in this way

rtk is a single Rust binary (MIT licensed, zero dependencies) that rewrites shell commands into optimized versions. When a command has no rtk equivalent, it passes through unchanged — zero risk of breaking existing workflows.

The integration follows three layers:

  1. Build time: scripts/download-rtk-binaries.js downloads platform-specific rtk and jq binaries into resources/binaries/ (bundled via existing asarUnpack: resources/**)
  2. First run: extractRtkBinaries() copies binaries from app resources to ~/.cherrystudio/bin/ (follows existing binary distribution pattern used by bun, uv, openclaw)
  3. Runtime: A PreToolUse hook in the Claude Code service intercepts Bash tool calls, runs rtk rewrite "<command>", and substitutes the optimized command if available

The following tradeoffs were made:

  • Binaries are bundled in the app package (increases app size ~5MB) rather than downloaded on demand — ensures rtk is always available without network dependency
  • jq is bundled alongside rtk for potential external hook script usage, even though the TypeScript hook doesn't need it
  • rtkRewrite() is async (non-blocking) to avoid stalling the main process event loop

The following alternatives were considered:

  • On-demand download (like bun/uv install scripts) — rejected because rtk should "always be on" per requirements
  • System PATH detection only — rejected because it requires users to install rtk manually

Breaking changes

None. The rtk rewrite is transparent and falls back gracefully when rtk is unavailable or a command has no optimized version.

Special notes for your reviewer

  • The download script is non-fatal: if binary download fails during build, the build continues without rtk
  • The hook runs before the existing preToolUseHook (permission handling), so commands are rewritten before permission checks
  • Version guard requires rtk >= 0.23.0 (when rtk rewrite subcommand was introduced)

Checklist

  • PR: The PR description is expressive enough and will help future contributors
  • Code: Write code that humans can understand and Keep it simple
  • Refactor: You have left the code cleaner than you found it (Boy Scout Rule)
  • Upgrade: Impact of this change on upgrade flows was considered and addressed if required
  • Documentation: A user-guide update was considered and is present (link) or not required
  • Self-review: I have reviewed my own code before requesting review from others

Release note

Integrate rtk to automatically optimize agent shell commands for 60-90% token savings

…ll commands

Bundle rtk and jq binaries into the app package at build time,
extract to ~/.cherrystudio/bin/ on first run, and add a PreToolUse
hook that rewrites Bash commands via `rtk rewrite` for 60-90% token
savings on common shell commands.

Closes #13600

Signed-off-by: Vaayne <liu.vaayne@gmail.com>
@vaayne vaayne marked this pull request as ready for review March 19, 2026 04:50
@DeJeune
Copy link
Collaborator

DeJeune commented Mar 19, 2026

ci failed

Signed-off-by: Vaayne <liu.vaayne@gmail.com>
@kangfenmao kangfenmao requested a review from beyondkmp March 19, 2026 09:25
'darwin-x64': { file: 'rtk-x86_64-apple-darwin.tar.gz', binary: 'rtk' },
'linux-x64': { file: 'rtk-x86_64-unknown-linux-musl.tar.gz', binary: 'rtk' },
'linux-arm64': { file: 'rtk-aarch64-unknown-linux-gnu.tar.gz', binary: 'rtk' },
'win32-x64': { file: 'rtk-x86_64-pc-windows-msvc.zip', binary: 'rtk.exe' }
Copy link
Collaborator

@beyondkmp beyondkmp Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This comment was translated by Claude.

No Windows ARM?


Original Content

没有 win arm 的?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rtk does not provide it.

}

function getBundledBinariesDir(): string {
const dir = path.join(getResourcePath(), 'binaries', getPlatformKey())
Copy link
Collaborator

@beyondkmp beyondkmp Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This issue/comment/review was translated by Claude.

If Windows ARM is not supported, consider filtering it out.


Original Content

windows arm 不支持的话,要考虑过滤掉

encoding: 'utf8',
stdio: ['pipe', 'pipe', 'pipe']
})
const match = output.match(/(\d+)\.(\d+)\.(\d+)/)
Copy link
Collaborator

@beyondkmp beyondkmp Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This issue/comment/review was translated by Claude.

It would be better to use the semver library.


Original Content

使用semver库更好。

// Download rtk and jq binaries for the target platform
try {
console.log(`Downloading rtk/jq binaries for ${platform}-${arch}...`)
execSync(`node "${path.join(__dirname, 'download-rtk-binaries.js')}" ${platform} ${arch}`, { stdio: 'inherit' })
Copy link
Collaborator

@beyondkmp beyondkmp Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This comment was translated by Claude.

This will cause an issue. When building packages for both architectures (arm/intel) on the same platform, the second package built will contain binaries for both architectures.


Original Content

这个会有问题,就是在一个平台上面打两个架构(arm/intel)的包的时候,后面打的那个包,会包含两个架构的 binary

Copy link
Collaborator

@beyondkmp beyondkmp Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This comment was translated by Claude.

It needs to be added to the file filter like the others, so that packages for other architectures can be filtered out.


Original Content

需要像其它的一样,加到 file filter 里面这样可以过滤掉其它架构的包

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in caca181 — added excludeRtkFilters in before-pack.js (lines 129-134) that excludes resources/binaries/<other-platform>/** for non-target platform-arch combinations, following the same pattern as excludeRipgrepFilters.

- Filter out unsupported win32-arm64 platform at runtime
- Use semver library for version comparison instead of manual parsing
- Add file filters in before-pack.js to exclude other-arch rtk binaries
- Remove unused jq binary from download script and extraction
- Make extractRtkBinaries and version check fully async
- Use version file for upgrade detection instead of file size
- Upgrade rewrite log level from debug to info
- Fix import consistency (node:os)
- Add unit tests for rtk utils

Signed-off-by: Vaayne <liu.vaayne@gmail.com>
@vaayne vaayne requested a review from beyondkmp March 19, 2026 13:48
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.

Feature Request: Support rtk for reducing LLM token consumption on agent shell commands

3 participants