From 85dbd4321a21ff126144e8aca29cf9581013ebca Mon Sep 17 00:00:00 2001 From: Greyforge Admin Date: Tue, 19 May 2026 21:53:56 -0400 Subject: [PATCH 1/2] Use platform shell for DAG commands --- src/cortex-cli/src/dag_cmd/executor.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/cortex-cli/src/dag_cmd/executor.rs b/src/cortex-cli/src/dag_cmd/executor.rs index e4145b0ea..4f65b439b 100644 --- a/src/cortex-cli/src/dag_cmd/executor.rs +++ b/src/cortex-cli/src/dag_cmd/executor.rs @@ -8,6 +8,14 @@ use crate::styled_output::print_info; use super::types::TaskExecutionResult; +fn shell_invocation() -> (&'static str, &'static str) { + if cfg!(windows) { + ("cmd.exe", "/C") + } else { + ("sh", "-c") + } +} + /// Task executor that runs the actual task commands. pub struct TaskExecutor { timeout: Duration, @@ -76,8 +84,9 @@ impl TaskExecutor { let timeout_duration = self.timeout; let result = tokio::time::timeout(timeout_duration, async { - let output = tokio::process::Command::new("sh") - .arg("-c") + let (shell, shell_arg) = shell_invocation(); + let output = tokio::process::Command::new(shell) + .arg(shell_arg) .arg(cmd) .output() .await From fbcedb8cf67bf25bcfddae7d372c475ea7282c63 Mon Sep 17 00:00:00 2001 From: Greyforge Admin Date: Tue, 19 May 2026 21:56:48 -0400 Subject: [PATCH 2/2] Honor DAG task working directories --- src/cortex-cli/src/dag_cmd/executor.rs | 20 +++++++++++++++----- src/cortex-cli/src/dag_cmd/helpers.rs | 4 ++++ src/cortex-cli/src/dag_cmd/tests.rs | 2 ++ src/cortex-cli/src/dag_cmd/types.rs | 3 +++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/cortex-cli/src/dag_cmd/executor.rs b/src/cortex-cli/src/dag_cmd/executor.rs index 4f65b439b..b4994597a 100644 --- a/src/cortex-cli/src/dag_cmd/executor.rs +++ b/src/cortex-cli/src/dag_cmd/executor.rs @@ -41,6 +41,11 @@ impl TaskExecutor { .get("command") .and_then(|v| v.as_str()) .map(String::from); + let working_dir = task + .metadata + .get("working_dir") + .and_then(|v| v.as_str()) + .map(String::from); if self.verbose { if let Some(ref cmd) = command { @@ -55,7 +60,7 @@ impl TaskExecutor { // If there's a command, execute it let (status, output, error) = if let Some(cmd) = command { - match self.run_command(&cmd).await { + match self.run_command(&cmd, working_dir.as_deref()).await { Ok(output) => (TaskStatus::Completed, Some(output), None), Err(e) => (TaskStatus::Failed, None, Some(e.to_string())), } @@ -80,14 +85,19 @@ impl TaskExecutor { } /// Run a shell command with timeout. - async fn run_command(&self, cmd: &str) -> Result { + async fn run_command(&self, cmd: &str, working_dir: Option<&str>) -> Result { let timeout_duration = self.timeout; let result = tokio::time::timeout(timeout_duration, async { let (shell, shell_arg) = shell_invocation(); - let output = tokio::process::Command::new(shell) - .arg(shell_arg) - .arg(cmd) + let mut command = tokio::process::Command::new(shell); + command.arg(shell_arg).arg(cmd); + + if let Some(dir) = working_dir { + command.current_dir(dir); + } + + let output = command .output() .await .context("Failed to execute command")?; diff --git a/src/cortex-cli/src/dag_cmd/helpers.rs b/src/cortex-cli/src/dag_cmd/helpers.rs index 7d0635486..042da2233 100644 --- a/src/cortex-cli/src/dag_cmd/helpers.rs +++ b/src/cortex-cli/src/dag_cmd/helpers.rs @@ -65,6 +65,10 @@ pub fn convert_specs(input: &DagSpecInput) -> Vec { spec = spec.with_metadata("command", serde_json::json!(cmd)); } + if let Some(working_dir) = &t.working_dir { + spec = spec.with_metadata("working_dir", serde_json::json!(working_dir)); + } + for (key, value) in &t.metadata { spec = spec.with_metadata(key, value.clone()); } diff --git a/src/cortex-cli/src/dag_cmd/tests.rs b/src/cortex-cli/src/dag_cmd/tests.rs index a2bc60cb4..d84f00de4 100644 --- a/src/cortex-cli/src/dag_cmd/tests.rs +++ b/src/cortex-cli/src/dag_cmd/tests.rs @@ -41,6 +41,7 @@ fn test_convert_specs() { name: "a".to_string(), description: "Task A".to_string(), command: Some("echo A".to_string()), + working_dir: None, depends_on: vec![], affected_files: vec![], priority: 10, @@ -51,6 +52,7 @@ fn test_convert_specs() { name: "b".to_string(), description: "Task B".to_string(), command: None, + working_dir: None, depends_on: vec!["a".to_string()], affected_files: vec!["file.txt".to_string()], priority: 5, diff --git a/src/cortex-cli/src/dag_cmd/types.rs b/src/cortex-cli/src/dag_cmd/types.rs index a72337b7a..5b0a63014 100644 --- a/src/cortex-cli/src/dag_cmd/types.rs +++ b/src/cortex-cli/src/dag_cmd/types.rs @@ -56,6 +56,9 @@ pub struct TaskSpecInput { /// Command to execute (optional). #[serde(default)] pub command: Option, + /// Working directory for command execution. + #[serde(default)] + pub working_dir: Option, /// Task dependencies (names of tasks). #[serde(default)] pub depends_on: Vec,