feat(cloud): add xh command support#1235
Conversation
|
|
Add rtk xh as a filter for the xh HTTP client (a modern curl replacement). Mirrors curl_cmd: prepends --pretty=none --body so the JSON schema filter sees raw payload, early-exits on non-2xx to avoid mangling HTML error pages. Also registers an RtkRule so the PreToolUse hook auto-rewrites bare 'xh ...' invocations to 'rtk xh ...' (Network category, ~70% savings). 5 unit tests added; full suite: 1355 passed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds first-class support for xh (HTTP client) to RTK’s CLI routing and auto-rewrite/discovery system, mirroring the existing curl filtering approach for JSON-aware compression.
Changes:
- Adds
rtk xhsubcommand wiring in the CLI (newCommands::Xhvariant + dispatch + operational categorization). - Introduces
src/cmds/cloud/xh_cmd.rsto runxhwith default flags and filter/truncate output (including the “schema only if shorter” guard related to #297). - Registers an
xhrewrite/discover rule so barexh ...can be rewritten tortk xh ....
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/main.rs |
Adds the Xh subcommand to Clap routing and operational command classification. |
src/discover/rules.rs |
Adds an RtkRule so xh invocations can be detected and rewritten to rtk xh. |
src/cmds/cloud/xh_cmd.rs |
Implements xh execution + JSON autodetection/schema filtering + truncation + unit tests. |
| // Early exit: don't feed HTTP error bodies (HTML 404 etc.) through JSON schema filter | ||
| if !output.status.success() { | ||
| let msg = if stderr.trim().is_empty() { | ||
| stdout.trim().to_string() | ||
| } else { | ||
| stderr.trim().to_string() | ||
| }; | ||
| eprintln!("FAILED: xh {}", msg); | ||
| return Ok(exit_code_from_output(&output, "xh")); | ||
| } |
There was a problem hiding this comment.
The “early exit” is keyed off the xh process exit status (output.status.success()), not the HTTP status code. As written, this won’t guarantee skipping filtering for non-2xx responses (as described), and can still run HTML error bodies through the JSON/schema path if xh exits 0 for HTTP 4xx/5xx. Consider either adding an xh flag that makes non-2xx exit non-zero (e.g., check-status) or capturing status/headers so you can decide based on the HTTP status explicitly.
| fn filter_xh_output(output: &str) -> String { | ||
| let trimmed = output.trim(); | ||
|
|
||
| // Try JSON detection: starts with { or [ | ||
| if (trimmed.starts_with('{') || trimmed.starts_with('[')) | ||
| && (trimmed.ends_with('}') || trimmed.ends_with(']')) | ||
| { | ||
| if let Ok(schema) = json_cmd::filter_json_string(trimmed, 5) { | ||
| // Only use schema if it's actually shorter than the original (#297) | ||
| if schema.len() <= trimmed.len() { | ||
| return schema; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Not JSON: truncate long output | ||
| let lines: Vec<&str> = trimmed.lines().collect(); | ||
| if lines.len() > 30 { | ||
| let mut result: Vec<&str> = lines[..30].to_vec(); | ||
| result.push(""); | ||
| let msg = format!( | ||
| "... ({} more lines, {} bytes total)", | ||
| lines.len() - 30, | ||
| trimmed.len() | ||
| ); | ||
| return format!("{}\n{}", result.join("\n"), msg); | ||
| } | ||
|
|
||
| // Short output: return as-is but truncate long lines | ||
| lines | ||
| .iter() | ||
| .map(|l| truncate(l, 200)) | ||
| .collect::<Vec<_>>() | ||
| .join("\n") | ||
| } |
There was a problem hiding this comment.
filter_xh_output is effectively identical to filter_curl_output (same JSON detection, schema-shorter guard for #297, and truncation logic). This duplication makes it easy for curl/xh behavior to drift over time; consider extracting the shared filtering into a common helper (e.g., a cloud/http response filter function) used by both commands.
Summary
rtk xhsubcommand — a filter wrappingxh(modern curl replacement at https://github.com/ducaale/xh).curl_cmd: prepends--pretty=none --bodyso the JSON schema filter sees raw payload; early-exits on non-2xx to avoid mangling HTML error pages.RtkRuleso the PreToolUse hook auto-rewrites barexh ...→rtk xh ...(Network category, ~70% est savings).Why
Many users (myself + global CLAUDE.md rules from others) already prefer
xhovercurl. Today those invocations bypass RTK entirely —rtk discoverflags them as top "unhandled commands" on sessions where I counted 22xhcalls in one session alone.Implementation
src/cmds/cloud/xh_cmd.rs— new, 5 unit tests (JSON schema compression, small-JSON passthrough Broken curl usage #297, plain text, line truncation, array)src/main.rs— import,Commands::Xhvariant, dispatch, read-only matcher entrysrc/discover/rules.rs— newRtkRuleright after thecurlruleTest plan
cargo build— clean (0 errors)cargo fmt— cleancargo test— 1355 passed, 0 failed (5 new)rtk xh https://r.jina.ai/.../users— 5729 B → 686 B (88% reduction)FAILED: xh …on stderr, no filter corruptionNotes
--pretty=none --bodyand xh honors later flags,rtk xh --headers <url>still works.curl_cmdbehavior.🤖 Generated with Claude Code