parse cd foo && ... for exec and apply_patch (#3083)

sometimes the model likes to run "cd foo && ..." instead of using the
workdir parameter of exec. handle them roughly the same.
This commit is contained in:
Jeremy Rose
2025-09-02 22:26:06 -07:00
committed by GitHub
parent b127a3643f
commit 53413c728e
5 changed files with 313 additions and 81 deletions

View File

@@ -235,6 +235,28 @@ mod tests {
);
}
#[test]
fn bash_cd_then_bar_is_same_as_bar() {
// Ensure a leading `cd` inside bash -lc is dropped when followed by another command.
assert_parsed(
&shlex_split_safe("bash -lc 'cd foo && bar'"),
vec![ParsedCommand::Unknown {
cmd: "bar".to_string(),
}],
);
}
#[test]
fn bash_cd_then_cat_is_read() {
assert_parsed(
&shlex_split_safe("bash -lc 'cd foo && cat foo.txt'"),
vec![ParsedCommand::Read {
cmd: "cat foo.txt".to_string(),
name: "foo.txt".to_string(),
}],
);
}
#[test]
fn supports_ls_with_pipe() {
let inner = "ls -la | sed -n '1,120p'";
@@ -1149,6 +1171,10 @@ fn parse_bash_lc_commands(original: &[String]) -> Option<Vec<ParsedCommand>> {
.collect();
if commands.len() > 1 {
commands.retain(|pc| !matches!(pc, ParsedCommand::Unknown { cmd } if cmd == "true"));
// Apply the same simplifications used for non-bash parsing, e.g., drop leading `cd`.
while let Some(next) = simplify_once(&commands) {
commands = next;
}
}
if commands.len() == 1 {
// If we reduced to a single command, attribute the full original script