diff --git a/apps/array/src/main/services/workspace/schemas.ts b/apps/array/src/main/services/workspace/schemas.ts index a1e88258..74c6a6e2 100644 --- a/apps/array/src/main/services/workspace/schemas.ts +++ b/apps/array/src/main/services/workspace/schemas.ts @@ -58,6 +58,7 @@ export const createWorkspaceInput = z.object({ folderPath: z.string().min(2, "Folder path must be a valid directory path"), mode: workspaceModeSchema, branch: z.string().optional(), + fetchLatest: z.boolean().optional(), }); export const deleteWorkspaceInput = z.object({ diff --git a/apps/array/src/main/services/workspace/service.ts b/apps/array/src/main/services/workspace/service.ts index 5f6e9f22..7c15a9f5 100644 --- a/apps/array/src/main/services/workspace/service.ts +++ b/apps/array/src/main/services/workspace/service.ts @@ -99,8 +99,15 @@ export class WorkspaceService extends TypedEventEmitter } async createWorkspace(options: CreateWorkspaceInput): Promise { - const { taskId, mainRepoPath, folderId, folderPath, mode, branch } = - options; + const { + taskId, + mainRepoPath, + folderId, + folderPath, + mode, + branch, + fetchLatest, + } = options; log.info( `Creating workspace for task ${taskId} in ${mainRepoPath} (mode: ${mode})`, ); @@ -276,6 +283,30 @@ export class WorkspaceService extends TypedEventEmitter ); } } + + // Fetch latest from origin if requested - pull from the base branch the user selected + if (fetchLatest) { + const baseBranch = branch ?? worktree.baseBranch; + if (baseBranch) { + try { + log.info( + `Fetching latest from origin/${baseBranch} for task ${taskId}`, + ); + await execAsync("git fetch origin", { cwd: worktree.worktreePath }); + await execAsync(`git pull origin ${baseBranch}`, { + cwd: worktree.worktreePath, + }); + log.info(`Successfully fetched latest for task ${taskId}`); + } catch (error) { + log.warn(`Failed to fetch latest for task ${taskId}:`, error); + // Don't fail workspace creation, just warn + } + } else { + log.info( + `Skipping fetch latest for task ${taskId}: no base branch specified`, + ); + } + } } catch (error) { log.error(`Failed to create worktree for task ${taskId}:`, error); throw new Error(`Failed to create worktree: ${String(error)}`); diff --git a/apps/array/src/renderer/features/settings/components/SettingsView.tsx b/apps/array/src/renderer/features/settings/components/SettingsView.tsx index 53dbd5e5..030aa112 100644 --- a/apps/array/src/renderer/features/settings/components/SettingsView.tsx +++ b/apps/array/src/renderer/features/settings/components/SettingsView.tsx @@ -57,10 +57,12 @@ export function SettingsView() { defaultRunMode, createPR, desktopNotifications, + fetchLatestOnNewTask, setAutoRunTasks, setDefaultRunMode, setCreatePR, setDesktopNotifications, + setFetchLatestOnNewTask, } = useSettingsStore(); const terminalLayoutMode = useTerminalLayoutStore( (state) => state.terminalLayoutMode, @@ -337,6 +339,23 @@ export function SettingsView() { size="1" /> + + + + + Fetch latest on new task + + + Pull the latest changes from origin after creating a + workspace + + + + diff --git a/apps/array/src/renderer/features/settings/stores/settingsStore.ts b/apps/array/src/renderer/features/settings/stores/settingsStore.ts index 70bc47b7..a2bf8403 100644 --- a/apps/array/src/renderer/features/settings/stores/settingsStore.ts +++ b/apps/array/src/renderer/features/settings/stores/settingsStore.ts @@ -20,6 +20,7 @@ interface SettingsStore { defaultModel: string; defaultFramework: AgentFramework; desktopNotifications: boolean; + fetchLatestOnNewTask: boolean; setAutoRunTasks: (autoRun: boolean) => void; setDefaultRunMode: (mode: DefaultRunMode) => void; @@ -30,6 +31,7 @@ interface SettingsStore { setDefaultModel: (model: string) => void; setDefaultFramework: (framework: AgentFramework) => void; setDesktopNotifications: (enabled: boolean) => void; + setFetchLatestOnNewTask: (enabled: boolean) => void; } export const useSettingsStore = create()( @@ -44,6 +46,7 @@ export const useSettingsStore = create()( defaultModel: DEFAULT_MODEL, defaultFramework: DEFAULT_FRAMEWORK, desktopNotifications: true, + fetchLatestOnNewTask: true, setAutoRunTasks: (autoRun) => set({ autoRunTasks: autoRun }), setDefaultRunMode: (mode) => set({ defaultRunMode: mode }), @@ -56,6 +59,8 @@ export const useSettingsStore = create()( setDefaultFramework: (framework) => set({ defaultFramework: framework }), setDesktopNotifications: (enabled) => set({ desktopNotifications: enabled }), + setFetchLatestOnNewTask: (enabled) => + set({ fetchLatestOnNewTask: enabled }), }), { name: "settings-storage", diff --git a/apps/array/src/renderer/sagas/task/task-creation.ts b/apps/array/src/renderer/sagas/task/task-creation.ts index 6ed1dfb6..4149774e 100644 --- a/apps/array/src/renderer/sagas/task/task-creation.ts +++ b/apps/array/src/renderer/sagas/task/task-creation.ts @@ -1,6 +1,7 @@ import type { PostHogAPIClient } from "@api/posthogClient"; import { buildPromptBlocks } from "@features/editor/utils/prompt-builder"; import { getSessionActions } from "@features/sessions/stores/sessionStore"; +import { useSettingsStore } from "@features/settings/stores/settingsStore"; import { useWorkspaceStore } from "@features/workspace/stores/workspaceStore"; import { logger } from "@renderer/lib/logger"; import { useTaskDirectoryStore } from "@renderer/stores/taskDirectoryStore"; @@ -108,6 +109,7 @@ export class TaskCreationSaga extends Saga< }); } + const fetchLatest = useSettingsStore.getState().fetchLatestOnNewTask; const workspaceInfo = await this.step({ name: "workspace_creation", execute: async () => { @@ -118,6 +120,7 @@ export class TaskCreationSaga extends Saga< folderPath: repoPath, mode: workspaceMode, branch: branch ?? undefined, + fetchLatest, }); }, rollback: async () => {