afd594ed6c
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>
107 lines
2.6 KiB
Go
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()
|
|
}
|