sketch: add fetch-on-launch flag for git fetch on startup

Defaults to true, i.e. no changes.

But now I can turn it off so that running Sketch
doesn't pop 1password asking for ssh keys.

Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s494a69769c279ad8k
diff --git a/cmd/sketch/main.go b/cmd/sketch/main.go
index 74571f7..5c9436a 100644
--- a/cmd/sketch/main.go
+++ b/cmd/sketch/main.go
@@ -264,28 +264,29 @@
 }
 
 type CLIFlags struct {
-	addr         string
-	skabandAddr  string
-	unsafe       bool
-	openBrowser  bool
-	httprrFile   string
-	maxDollars   float64
-	oneShot      bool
-	prompt       string
-	modelName    string
-	llmAPIKey    string
-	listModels   bool
-	verbose      bool
-	version      bool
-	workingDir   string
-	dumpDist     string
-	sshPort      int
-	forceRebuild bool
-	baseImage    string
-	linkToGitHub bool
-	ignoreSig    bool
-	doUpdate     bool
-	checkVersion bool
+	addr          string
+	skabandAddr   string
+	unsafe        bool
+	openBrowser   bool
+	httprrFile    string
+	maxDollars    float64
+	oneShot       bool
+	prompt        string
+	modelName     string
+	llmAPIKey     string
+	listModels    bool
+	verbose       bool
+	version       bool
+	workingDir    string
+	dumpDist      string
+	sshPort       int
+	forceRebuild  bool
+	baseImage     string
+	linkToGitHub  bool
+	ignoreSig     bool
+	doUpdate      bool
+	checkVersion  bool
+	fetchOnLaunch bool
 
 	gitUsername         string
 	gitEmail            string
@@ -344,6 +345,7 @@
 	userFlags.BoolVar(&flags.version, "version", false, "print the version and exit")
 	userFlags.BoolVar(&flags.doUpdate, "update", false, "update to the latest version of sketch")
 	userFlags.BoolVar(&flags.checkVersion, "version-check", true, "do version upgrade check (please leave this on)")
+	userFlags.BoolVar(&flags.fetchOnLaunch, "fetch-on-launch", true, "do a git fetch when sketch starts")
 	userFlags.IntVar(&flags.sshPort, "ssh-port", 0, "the host port number that the container's ssh server will listen on, or a randomly chosen port if this value is 0")
 	userFlags.BoolVar(&flags.forceRebuild, "force-rebuild-container", false, "rebuild Docker container")
 	userFlags.BoolVar(&flags.forceRebuild, "rebuild", false, "rebuild Docker container (alias for -force-rebuild-container)")
@@ -555,6 +557,7 @@
 		MCPServers:          flags.mcpServers,
 		PassthroughUpstream: flags.passthroughUpstream,
 		DumpLLM:             flags.dumpLLM,
+		FetchOnLaunch:       flags.fetchOnLaunch,
 	}
 
 	if err := dockerimg.LaunchContainer(ctx, config); err != nil {
@@ -699,6 +702,7 @@
 		SSHConnectionString: flags.sshConnectionString,
 		MCPServers:          flags.mcpServers,
 		PassthroughUpstream: flags.passthroughUpstream,
+		FetchOnLaunch:       flags.fetchOnLaunch,
 	}
 
 	// Parse timeout configuration
diff --git a/dockerimg/dockerimg.go b/dockerimg/dockerimg.go
index 49dafa0..ba93ed0 100644
--- a/dockerimg/dockerimg.go
+++ b/dockerimg/dockerimg.go
@@ -142,6 +142,9 @@
 
 	// DumpLLM requests dumping of raw communications with LLM services to files
 	DumpLLM bool
+
+	// FetchOnLaunch enables git fetch during initialization
+	FetchOnLaunch bool
 }
 
 // LaunchContainer creates a docker container for a project, installs sketch and opens a connection to it.
@@ -646,6 +649,9 @@
 	if config.DumpLLM {
 		cmdArgs = append(cmdArgs, "-dump-llm")
 	}
+	if !config.FetchOnLaunch {
+		cmdArgs = append(cmdArgs, "-fetch-on-launch=false")
+	}
 
 	// Add additional docker arguments if provided
 	if config.DockerArgs != "" {
diff --git a/loop/agent.go b/loop/agent.go
index 872f77f..da0f847 100644
--- a/loop/agent.go
+++ b/loop/agent.go
@@ -1130,6 +1130,8 @@
 	BashTimeouts *claudetool.Timeouts
 	// PassthroughUpstream configures upstream remote for passthrough to innie
 	PassthroughUpstream bool
+	// FetchOnLaunch enables git fetch during initialization
+	FetchOnLaunch bool
 }
 
 // NewAgent creates a new Agent.
@@ -1242,15 +1244,16 @@
 
 	// If a commit was specified, we fetch and reset to it.
 	if a.config.Commit != "" && a.gitState.gitRemoteAddr != "" {
-		slog.InfoContext(ctx, "updating git repo", "commit", a.config.Commit)
-
-		cmd := exec.CommandContext(ctx, "git", "fetch", "--prune", "origin")
-		cmd.Dir = a.workingDir
-		if out, err := cmd.CombinedOutput(); err != nil {
-			return fmt.Errorf("git fetch: %s: %w", out, err)
+		if a.config.FetchOnLaunch {
+			slog.InfoContext(ctx, "updating git repo", "commit", a.config.Commit)
+			cmd := exec.CommandContext(ctx, "git", "fetch", "--prune", "origin")
+			cmd.Dir = a.workingDir
+			if out, err := cmd.CombinedOutput(); err != nil {
+				return fmt.Errorf("git fetch: %s: %w", out, err)
+			}
 		}
 		// The -B resets the branch if it already exists (or creates it if it doesn't)
-		cmd = exec.CommandContext(ctx, "git", "checkout", "-f", "-B", "sketch-wip", a.config.Commit)
+		cmd := exec.CommandContext(ctx, "git", "checkout", "-f", "-B", "sketch-wip", a.config.Commit)
 		cmd.Dir = a.workingDir
 		if checkoutOut, err := cmd.CombinedOutput(); err != nil {
 			// Remove git hooks if they exist and retry