aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/next/CHANGELOG.md1
-rw-r--r--src/platform/windows.rs57
2 files changed, 43 insertions, 15 deletions
diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md
index d3f9f8e9..259e2455 100644
--- a/docs/next/CHANGELOG.md
+++ b/docs/next/CHANGELOG.md
@@ -7,6 +7,7 @@
- Relicensed Herdr from AGPL-3.0-or-later to Apache-2.0.
### Fixed
+- Windows `agent start` now launches agents without native arguments instead of timing out on an invalid empty PowerShell argument list. (#2072)
- Vibe and other Kitty-keyboard pane applications now receive shifted letters and punctuation when they request associated text. (#2020)
- Linux runtimes without terminal foreground process groups can opt into child-group agent detection with `HERDR_PROCESS_DETECTION=child-groups`. (#1982)
- Installing the Herdr agent skill with the `skills` CLI no longer copies the entire repository. (#2022)
diff --git a/src/platform/windows.rs b/src/platform/windows.rs
index 705a1d1f..48b31c89 100644
--- a/src/platform/windows.rs
+++ b/src/platform/windows.rs
@@ -119,6 +119,10 @@ pub(crate) fn interactive_shell_command(argv: &[String], shell_name: &str) -> Op
fn powershell_agent_script(argv: &[String]) -> Option<String> {
let (program, args) = argv.split_first()?;
+ if args.is_empty() {
+ return Some(format!("& {}", super::quote_powershell_arg(program)));
+ }
+
let command_line = args
.iter()
.map(|arg| quote_windows_command_line_arg(arg))
@@ -1219,6 +1223,16 @@ mod tests {
}
#[test]
+ fn powershell_agent_command_omits_argument_list_when_no_arguments_are_passed() {
+ let argv = vec!["opencode".into()];
+
+ assert_eq!(
+ super::interactive_shell_command(&argv, "powershell.exe").as_deref(),
+ Some("& opencode")
+ );
+ }
+
+ #[test]
fn cmd_agent_command_encodes_edge_arguments_without_cmd_expansion() {
use base64::Engine as _;
@@ -1275,25 +1289,38 @@ mod tests {
];
let inherited_path = std::env::var_os("PATH").unwrap_or_default();
let path = format!("{};{}", base.display(), inherited_path.to_string_lossy());
+ let run_command = |shell: &str, command: &str, capture: &std::path::Path| {
+ let mut process = if shell == "cmd.exe" {
+ let mut process = Command::new("cmd.exe");
+ process.args(["/d", "/c", command]);
+ process
+ } else {
+ let mut process = Command::new("powershell.exe");
+ process.args(["-NoLogo", "-NoProfile", "-Command", command]);
+ process
+ };
+ process
+ .env("PATH", &path)
+ .env("HERDR_ARGV_CAPTURE", capture)
+ .status()
+ .unwrap()
+ };
for shell in ["powershell.exe", "cmd.exe"] {
+ let no_args_capture = base.join(format!("{shell}-no-args.txt"));
+ let no_args_command = super::interactive_shell_command(&["pi".into()], shell).unwrap();
+ let status = run_command(shell, &no_args_command, &no_args_capture);
+ assert!(status.success(), "{shell} argument-free command failed");
+ assert_eq!(
+ fs::read_to_string(no_args_capture)
+ .unwrap()
+ .replace("\r\n", "\n"),
+ "\n\n\n\n\n\n"
+ );
+
let capture = base.join(format!("{shell}.txt"));
let command = super::interactive_shell_command(&argv, shell).unwrap();
- let status = if shell == "cmd.exe" {
- Command::new("cmd.exe")
- .args(["/d", "/c", &command])
- .env("PATH", &path)
- .env("HERDR_ARGV_CAPTURE", &capture)
- .status()
- .unwrap()
- } else {
- Command::new("powershell.exe")
- .args(["-NoLogo", "-NoProfile", "-Command", &command])
- .env("PATH", &path)
- .env("HERDR_ARGV_CAPTURE", &capture)
- .status()
- .unwrap()
- };
+ let status = run_command(shell, &command, &capture);
assert!(status.success(), "{shell} command failed");
assert_eq!(
fs::read_to_string(capture).unwrap().replace("\r\n", "\n"),