diff --git a/openless-all/app/crates/openless-core/src/coding_agent.rs b/openless-all/app/crates/openless-core/src/coding_agent.rs index b1283e8ff..6227b2018 100644 --- a/openless-all/app/crates/openless-core/src/coding_agent.rs +++ b/openless-all/app/crates/openless-core/src/coding_agent.rs @@ -604,14 +604,20 @@ pub fn build_dsh_patch_yaml(patch_path: &Path, prompt: &str) -> Result Option { .lines() .map(str::trim) .filter(|line| !line.is_empty()); + // A crashing Node CLI prints "Error: " and a stack, then ends with + // "Node.js vX" — taking only the last line would show just the version. lines .clone() - .find(|line| line.starts_with("dsh:") || line.contains("error:")) + .find(|line| line.starts_with("dsh:") || line.to_ascii_lowercase().contains("error:")) .or_else(|| lines.next_back()) .map(str::to_string) } @@ -2386,6 +2394,54 @@ mod tests { } } + #[test] + fn summarize_stderr_picks_the_node_crash_message_not_the_version_footer() { + let stderr = "file:///D:/dsh/lib/index.js:3564\n\ +\t\tthrow new Error(`${binName}: failed to parse ${label} ${file}: ${String(error)}`);\n\ +\t\t ^\n\ +\n\ +Error: dsh: failed to parse overlay C:\\Temp\\openless.patch.yml: YAMLException: bad indentation\n\ + at parsePatchList (file:///D:/dsh/lib/index.js:3564:9)\n\ +\n\ +Node.js v24.18.1\n"; + + assert_eq!( + summarize_stderr(stderr).as_deref(), + Some("Error: dsh: failed to parse overlay C:\\Temp\\openless.patch.yml: YAMLException: bad indentation") + ); + } + + #[test] + fn summarize_stderr_falls_back_to_the_last_line() { + assert_eq!( + summarize_stderr("starting\nsomething odd happened\n").as_deref(), + Some("something odd happened") + ); + assert_eq!(summarize_stderr("\n \n"), None); + } + + #[test] + fn dsh_patch_yaml_keeps_its_indentation() { + let yaml = build_dsh_patch_yaml( + Path::new(r"C:\Users\me\AppData\Local\Temp\dsh-events.mjs"), + "第一行\n\"第二行\"", + ) + .unwrap(); + + assert_eq!( + yaml, + concat!( + "# Generated by OpenLess. Applied as the last patch layer for one headless run.\n", + "- insert:\n", + " - id: dsh-events\n", + " name: \"C:\\\\Users\\\\me\\\\AppData\\\\Local\\\\Temp\\\\dsh-events.mjs\"\n", + "- id: headless-runner\n", + " config:\n", + " task: \"第一行\\n\\\"第二行\\\"\"\n", + ) + ); + } + #[test] fn materialization_plan_substitutes_plain_and_json_paths() { let directory = std::env::temp_dir().join("openless-agent-plan");