Skip to content

Commit 346624b

Browse files
committed
feat(cli): support piped stdin as query input
1 parent 4beafc1 commit 346624b

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

crates/rullm-cli/src/cli_helpers.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//! the full rullm-cli crate.
66
77
use anyhow::Result;
8+
use std::io::Read;
89

910
/// Helper function to resolve model priority: global CLI model, command-specific model, or default
1011
///
@@ -47,3 +48,46 @@ pub fn resolve_direct_query_model(
4748
)
4849
})
4950
}
51+
52+
/// Merges piped stdin and an optional query argument into a single query string.
53+
///
54+
/// - If stdin is piped and contains data, reads it.
55+
/// - If a query argument is also provided, appends it to the stdin content (with a newline if needed).
56+
/// - If only stdin is present, returns its content.
57+
/// - If only a query argument is present, returns it.
58+
/// - If neither is present, returns None.
59+
///
60+
/// This is used to support CLI usage like:
61+
/// cat foo.py | rullm 'explain this' // stdin + arg
62+
/// cat foo.py | rullm // stdin only
63+
/// rullm 'explain this' // arg only
64+
pub fn merge_stdin_and_query(query: Option<String>) -> Option<String> {
65+
let mut stdin_buf = String::new();
66+
let stdin_piped = !atty::is(atty::Stream::Stdin)
67+
&& std::io::stdin().read_to_string(&mut stdin_buf).is_ok()
68+
&& !stdin_buf.trim().is_empty();
69+
70+
match (stdin_piped, query) {
71+
(true, Some(arg_query)) => {
72+
// Both stdin and query arg: append arg to stdin
73+
let combined = if stdin_buf.ends_with('\n') {
74+
format!("{stdin_buf}{arg_query}")
75+
} else {
76+
format!("{stdin_buf}\n{arg_query}")
77+
};
78+
Some(combined)
79+
}
80+
(true, None) => {
81+
// Only stdin
82+
Some(stdin_buf)
83+
}
84+
(false, Some(arg_query)) => {
85+
// Only query arg
86+
Some(arg_query)
87+
}
88+
(false, None) => {
89+
// Neither
90+
None
91+
}
92+
}
93+
}

crates/rullm-cli/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use commands::Commands;
2121
use output::OutputLevel;
2222
use templates::resolve_template_prompts;
2323

24+
use crate::cli_helpers::merge_stdin_and_query;
25+
2426
#[tokio::main]
2527
async fn main() -> Result<()> {
2628
run().await
@@ -31,7 +33,8 @@ async fn main() -> Result<()> {
3133
pub async fn run() -> Result<()> {
3234
// Enable shell completion generation when the user sets COMPLETE=fish etc.
3335
clap_complete::CompleteEnv::with_factory(Cli::command).complete();
34-
let cli = Cli::parse();
36+
let mut cli = Cli::parse();
37+
cli.query = merge_stdin_and_query(cli.query.take());
3538

3639
let mut cli_config = CliConfig::load();
3740

0 commit comments

Comments
 (0)