sketch: add -ignoresig flag to ignore SIGINT and SIGTERM

Inside the container, do not let the agent kill PID 1.

Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s5197e5ad119cd474k
diff --git a/cmd/sketch/main.go b/cmd/sketch/main.go
index ef06e49..d56fe7a 100644
--- a/cmd/sketch/main.go
+++ b/cmd/sketch/main.go
@@ -12,10 +12,12 @@
 	"net/http"
 	"os"
 	"os/exec"
+	"os/signal"
 	"path/filepath"
 	"runtime"
 	"runtime/debug"
 	"strings"
+	"syscall"
 
 	"sketch.dev/experiment"
 	"sketch.dev/llm"
@@ -50,6 +52,11 @@
 func run() error {
 	flagArgs := parseCLIFlags()
 
+	// Set up signal handling if -ignoresig flag is set
+	if flagArgs.ignoreSig {
+		setupSignalIgnoring()
+	}
+
 	if flagArgs.version {
 		bi, ok := debug.ReadBuildInfo()
 		if ok {
@@ -196,6 +203,7 @@
 	sshPort      int
 	forceRebuild bool
 	linkToGitHub bool
+	ignoreSig    bool
 
 	gitUsername         string
 	gitEmail            string
@@ -251,6 +259,7 @@
 	userFlags.Var(&flags.mounts, "mount", "volume to mount in the container in format /path/on/host:/path/in/container (can be repeated)")
 	userFlags.BoolVar(&flags.termUI, "termui", true, "enable terminal UI")
 	userFlags.StringVar(&flags.branchPrefix, "branch-prefix", "sketch/", "prefix for git branches created by sketch")
+	userFlags.BoolVar(&flags.ignoreSig, "ignoresig", false, "ignore typical termination signals (SIGINT, SIGTERM)")
 	userFlags.Var(&flags.mcpServers, "mcp", "MCP server configuration as JSON (can be repeated). Schema: {\"name\": \"server-name\", \"type\": \"stdio|http|sse\", \"url\": \"...\", \"command\": \"...\", \"args\": [...], \"env\": {...}, \"headers\": {...}}")
 
 	// Internal flags (for sketch developers or internal use)
@@ -834,3 +843,25 @@
 	fmt.Printf("Successfully dumped embedded /dist/ filesystem to %q\n", outputDir)
 	return nil
 }
+
+// setupSignalIgnoring sets up signal handling to ignore SIGINT and SIGTERM
+// when the -ignoresig flag is used. This prevents the typical Ctrl+C or
+// termination signals from killing the process.
+func setupSignalIgnoring() {
+	// Create a channel to receive signals
+	sigChan := make(chan os.Signal, 1)
+
+	// Register the channel to receive specific signals
+	// We ignore SIGINT (Ctrl+C) and SIGTERM (termination request)
+	// but allow SIGQUIT to still work for debugging/stack dumps
+	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
+
+	// Start a goroutine to handle the signals
+	go func() {
+		for sig := range sigChan {
+			// Simply ignore the signal by doing nothing
+			// This prevents the default behavior of terminating the process
+			_ = sig // Suppress unused variable warning
+		}
+	}()
+}