diff --git a/internal/criteria/db.go b/internal/criteria/db.go index 96f5b0a..d665114 100644 --- a/internal/criteria/db.go +++ b/internal/criteria/db.go @@ -5,12 +5,13 @@ import ( "context" "fmt" "os" - "os/exec" "path/filepath" "strings" "time" "github.com/jackc/pgx/v5" + + "github.com/tzone85/nexus-dispatch/internal/shellexec" ) // readDatabaseURL returns the DATABASE_URL value from .nxd-db/connect.env in workDir, @@ -43,7 +44,11 @@ func evaluateMigrationSucceeds(ctx context.Context, workDir string, c Criterion) return Result{Criterion: c, Passed: false, Message: "no .nxd-db/connect.env in worktree — devdb not provisioned for this story"} } - cmd := exec.CommandContext(ctx, "sh", "-c", c.Command) + // shellexec.CommandContext picks the right shell per OS (sh on Unix, + // cmd.exe on Windows; NXD_SHELL overrides). Direct exec.Command("sh", ...) + // would silently fail on native Windows even for read-only criteria + // evaluation against an externally-provisioned DB. + cmd := shellexec.CommandContext(ctx, c.Command) cmd.Dir = workDir cmd.Env = append(os.Environ(), "DATABASE_URL="+dsn) out, err := cmd.CombinedOutput() diff --git a/internal/engine/investigator.go b/internal/engine/investigator.go index acf9f66..ed6e3d6 100644 --- a/internal/engine/investigator.go +++ b/internal/engine/investigator.go @@ -5,12 +5,12 @@ import ( "encoding/json" "fmt" "os" - "os/exec" "path/filepath" "strings" "github.com/tzone85/nexus-dispatch/internal/agent" "github.com/tzone85/nexus-dispatch/internal/llm" + "github.com/tzone85/nexus-dispatch/internal/shellexec" ) const ( @@ -266,7 +266,12 @@ func (inv *Investigator) handleRunCommand(ctx context.Context, repoPath string, return fmt.Sprintf("error: command not in allowlist: %s", params.Command) } - cmd := exec.CommandContext(ctx, "sh", "-c", params.Command) + // Investigator commands come from the operator-configured allowlist + // (see isCommandAllowed above). Route through shellexec so Windows + // pipelines (cmd.exe) and NXD_SHELL overrides work the same as the + // gemma runtime's run_command tool — direct `sh -c` would silently + // fail on native Windows. + cmd := shellexec.CommandContext(ctx, params.Command) cmd.Dir = repoPath output, err := cmd.CombinedOutput()