Files
ctask/internal/shell/launch.go
T
typebasedio afd594ed6c feat: platform-specific shell and agent launch helpers
DefaultShell, ExecAgent, ExecShell with prompt prefix. Banner, container notice. Tests for all helpers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:32:55 -04:00

107 lines
2.6 KiB
Go

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.
func BannerLines(mode, slug, wsPath string) []string {
return []string{
fmt.Sprintf("[ctask] %s :: %s", mode, slug),
fmt.Sprintf("[ctask] %s", wsPath),
}
}
// 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()
}