package shell import ( "fmt" "os" "os/exec" "runtime" ) // DefaultShell returns the platform-appropriate interactive shell. func DefaultShell() string { if runtime.GOOS == "windows" { if _, err := exec.LookPath("powershell.exe"); err == nil { return "powershell.exe" } return "cmd.exe" } shell := os.Getenv("SHELL") if shell != "" { return shell } return "bash" } // BuildEnvList merges ctask env vars into the current process environment. func BuildEnvList(vars map[string]string) []string { env := os.Environ() for k, v := range vars { env = append(env, fmt.Sprintf("%s=%s", k, v)) } return env } // PromptPrefix returns the shell prompt prefix for --shell mode. func PromptPrefix(slug, mode string) string { return fmt.Sprintf("(ctask:%s|%s) ", slug, mode) } // BannerLines returns the launch banner lines for agent mode. // Adds a "project dir:" line when launchDir is non-empty. func BannerLines(mode, slug, wsPath, launchDir string) []string { lines := []string{ fmt.Sprintf("[ctask] %s :: %s", mode, slug), fmt.Sprintf("[ctask] %s", wsPath), } if launchDir != "" { lines = append(lines, fmt.Sprintf("[ctask] project dir: %s/", launchDir)) } return lines } // ContainerNotice returns the v0.1 deferred container mode message. func ContainerNotice() string { return "[ctask] container mode is not available in v0.1. Use local mode or see docs for manual container setup." } // ExecAgent launches the agent command in the workspace directory. func ExecAgent(agent string, wsDir string, envVars map[string]string) error { path, err := exec.LookPath(agent) if err != nil { return fmt.Errorf("agent command not found: %s", agent) } cmd := exec.Command(path) cmd.Dir = wsDir cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Env = BuildEnvList(envVars) return cmd.Run() } // ExecShell launches an interactive shell in the workspace directory. func ExecShell(wsDir string, envVars map[string]string, slug, mode string) error { shellCmd := DefaultShell() prefix := PromptPrefix(slug, mode) var cmd *exec.Cmd if runtime.GOOS == "windows" { if shellCmd == "powershell.exe" { cmd = exec.Command(shellCmd, "-NoExit", "-Command", fmt.Sprintf("function prompt { '%s' + (Get-Location).Path + '> ' }", prefix)) } else { // cmd.exe cmd = exec.Command(shellCmd) } } else { cmd = exec.Command(shellCmd) } cmd.Dir = wsDir cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr env := BuildEnvList(envVars) if runtime.GOOS == "windows" && shellCmd == "cmd.exe" { env = append(env, fmt.Sprintf("PROMPT=%s$P$G", prefix)) } else if runtime.GOOS != "windows" { existingPS1 := os.Getenv("PS1") if existingPS1 == "" { existingPS1 = "\\u@\\h:\\w\\$ " } env = append(env, fmt.Sprintf("PS1=%s%s", prefix, existingPS1)) } cmd.Env = env return cmd.Run() }