diff --git a/packages/ui/src/components/multirun/MultiRunLauncher.tsx b/packages/ui/src/components/multirun/MultiRunLauncher.tsx index aea4441a..27977fdd 100644 --- a/packages/ui/src/components/multirun/MultiRunLauncher.tsx +++ b/packages/ui/src/components/multirun/MultiRunLauncher.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { toast } from '@/components/ui'; import { Button } from '@/components/ui/button'; +import { Checkbox } from '@/components/ui/checkbox'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; @@ -105,6 +106,7 @@ export const MultiRunLauncher: React.FC = ({ const [setupCommands, setSetupCommands] = React.useState([]); const [isSetupCommandsOpen, setIsSetupCommandsOpen] = React.useState(false); const [isLoadingSetupCommands, setIsLoadingSetupCommands] = React.useState(false); + const [isolateRuns, setIsolateRuns] = React.useState(true); const [showFileMention, setShowFileMention] = React.useState(false); const [mentionQuery, setMentionQuery] = React.useState(''); const [showCommandAutocomplete, setShowCommandAutocomplete] = React.useState(false); @@ -301,6 +303,22 @@ export const MultiRunLauncher: React.FC = ({ // Use the BranchSelector hook for branch state management const [worktreeBaseBranch, setWorktreeBaseBranch] = React.useState(''); const { isLoading: isLoadingWorktreeBaseBranches, isGitRepository } = useBranchOptions(selectedProjectDirectory); + const wasIsolationDisabledByNonGitRef = React.useRef(false); + const canIsolateRuns = isGitRepository === true; + const effectiveIsolateRuns = canIsolateRuns && isolateRuns; + + React.useEffect(() => { + if (isGitRepository === false) { + wasIsolationDisabledByNonGitRef.current = true; + setIsolateRuns(false); + return; + } + + if (isGitRepository === true && wasIsolationDisabledByNonGitRef.current) { + wasIsolationDisabledByNonGitRef.current = false; + setIsolateRuns(true); + } + }, [isGitRepository]); const createMultiRun = useMultiRunStore((state) => state.createMultiRun); const error = useMultiRunStore((state) => state.error); @@ -567,7 +585,8 @@ export const MultiRunLauncher: React.FC = ({ prompt: prompt.trim(), models: modelsForStore, agent: selectedAgent || undefined, - worktreeBaseBranch, + worktreeBaseBranch: effectiveIsolateRuns ? worktreeBaseBranch : undefined, + isolateRuns: effectiveIsolateRuns, files: filesForStore.length > 0 ? filesForStore : undefined, setupCommands: commandsForStore.length > 0 ? commandsForStore : undefined, }; @@ -592,7 +611,7 @@ export const MultiRunLauncher: React.FC = ({ selectedModels.length >= 2 && selectedProjectDirectory && !isLoadingWorktreeBaseBranches && - (isGitRepository === false || (isGitRepository === true && worktreeBaseBranch)) + (isGitRepository === false || !effectiveIsolateRuns || worktreeBaseBranch) ); const configuredSetupCount = setupCommands.filter(cmd => cmd.trim()).length; @@ -698,8 +717,18 @@ export const MultiRunLauncher: React.FC = ({ directory={selectedProjectDirectory} value={worktreeBaseBranch} onChange={setWorktreeBaseBranch} + disabled={!effectiveIsolateRuns} id="multirun-worktree-base-branch" /> + {/* Agent */} @@ -934,7 +963,7 @@ export const MultiRunLauncher: React.FC = ({ {/* ── Fixed footer ── */}
- {isGitRepository === false ? ( + {!effectiveIsolateRuns && isGitRepository !== null ? (
{t('multirun.launcher.project.gitRequired')} diff --git a/packages/ui/src/lib/i18n/messages/en.ts b/packages/ui/src/lib/i18n/messages/en.ts index e57bbfcf..545fbbc1 100644 --- a/packages/ui/src/lib/i18n/messages/en.ts +++ b/packages/ui/src/lib/i18n/messages/en.ts @@ -151,6 +151,7 @@ export const dict = { 'multirun.launcher.groupName.placeholder': 'feature-auth, bugfix-login', 'multirun.launcher.baseBranch.label': 'Base branch', 'multirun.launcher.baseBranch.info': 'New branch created from this base per model', + 'multirun.launcher.isolateRuns.label': 'Isolate runs', 'multirun.launcher.agent.label': 'Agent', 'multirun.launcher.agent.info': 'Agent used for all runs. Defaults to your configured agent.', 'multirun.launcher.setupCommands.label': 'Setup commands', diff --git a/packages/ui/src/lib/i18n/messages/es.ts b/packages/ui/src/lib/i18n/messages/es.ts index 7929efa1..2c09a287 100644 --- a/packages/ui/src/lib/i18n/messages/es.ts +++ b/packages/ui/src/lib/i18n/messages/es.ts @@ -152,6 +152,7 @@ export const dict: Record = { "multirun.launcher.groupName.placeholder": "feature-auth, bugfix-login", "multirun.launcher.baseBranch.label": "Rama base", "multirun.launcher.baseBranch.info": "Nueva rama creada desde esta base por modelo", + "multirun.launcher.isolateRuns.label": "Aislar ejecuciones", "multirun.launcher.agent.label": "Agente", "multirun.launcher.agent.info": "Agente usado para todas las ejecuciones. Por defecto es el agente configurado.", "multirun.launcher.setupCommands.label": "Comandos de configuración", diff --git a/packages/ui/src/lib/i18n/messages/ko.ts b/packages/ui/src/lib/i18n/messages/ko.ts index 86d127dd..a182e6ac 100644 --- a/packages/ui/src/lib/i18n/messages/ko.ts +++ b/packages/ui/src/lib/i18n/messages/ko.ts @@ -152,6 +152,7 @@ export const dict: Record = { 'multirun.launcher.groupName.placeholder': 'feature-auth, bugfix-login', 'multirun.launcher.baseBranch.label': '기본 브랜치', 'multirun.launcher.baseBranch.info': '각 모델은 이 기본 브랜치에서 새 브랜치를 만듭니다', + 'multirun.launcher.isolateRuns.label': '실행 격리', 'multirun.launcher.agent.label': '에이전트', 'multirun.launcher.agent.info': '모든 실행에 사용할 에이전트입니다. 설정된 기본 에이전트를 사용합니다.', 'multirun.launcher.setupCommands.label': '셋업 명령', diff --git a/packages/ui/src/lib/i18n/messages/pl.ts b/packages/ui/src/lib/i18n/messages/pl.ts index 8fb7f046..759922db 100644 --- a/packages/ui/src/lib/i18n/messages/pl.ts +++ b/packages/ui/src/lib/i18n/messages/pl.ts @@ -227,6 +227,7 @@ export const dict: Record = { 'multirun.launcher.groupName.placeholder': 'feature-auth, bugfix-login', 'multirun.launcher.baseBranch.label': 'Gałąź bazowa', 'multirun.launcher.baseBranch.info': 'Nowa gałąź tworzona z tej bazy dla każdego modelu', + 'multirun.launcher.isolateRuns.label': 'Izoluj uruchomienia', 'multirun.launcher.agent.label': 'Agent', 'multirun.launcher.agent.info': 'Agent używany dla wszystkich uruchomień. Domyślnie twój skonfigurowany agent.', 'multirun.launcher.setupCommands.label': 'Polecenia konfiguracji', diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.ts b/packages/ui/src/lib/i18n/messages/pt-BR.ts index 8fb6a34a..ed5a48e3 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.ts @@ -152,6 +152,7 @@ export const dict: Record = { "multirun.launcher.groupName.placeholder": "feature-auth, bugfix-login", "multirun.launcher.baseBranch.label": "Branch base", "multirun.launcher.baseBranch.info": "Cria uma nova branch a partir desta base para cada modelo", + "multirun.launcher.isolateRuns.label": "Isolar execuções", "multirun.launcher.agent.label": "Agente", "multirun.launcher.agent.info": "Agente usado para todas as execuções. Por padrão, usa o agente configurado.", "multirun.launcher.setupCommands.label": "Comandos de configuração", diff --git a/packages/ui/src/lib/i18n/messages/uk.ts b/packages/ui/src/lib/i18n/messages/uk.ts index 58aa0f85..490c8781 100644 --- a/packages/ui/src/lib/i18n/messages/uk.ts +++ b/packages/ui/src/lib/i18n/messages/uk.ts @@ -152,6 +152,7 @@ export const dict: Record = { "multirun.launcher.groupName.placeholder": "feature-auth, bugfix-login", "multirun.launcher.baseBranch.label": "Базова гілка", "multirun.launcher.baseBranch.info": "Нова гілка, створена з цієї бази для кожної моделі", + "multirun.launcher.isolateRuns.label": "Ізолювати запуски", "multirun.launcher.agent.label": "Агент", "multirun.launcher.agent.info": "Агент використовується для всіх запусків. За замовчуванням налаштований агент.", "multirun.launcher.setupCommands.label": "Команди налаштування", diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.ts b/packages/ui/src/lib/i18n/messages/zh-CN.ts index 90323bc8..c988a171 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.ts @@ -152,6 +152,7 @@ export const dict: Record = { 'multirun.launcher.groupName.placeholder': 'feature-auth, bugfix-login', 'multirun.launcher.baseBranch.label': '基础分支', 'multirun.launcher.baseBranch.info': '每个模型都会基于此分支创建新分支', + 'multirun.launcher.isolateRuns.label': '隔离运行', 'multirun.launcher.agent.label': '智能体', 'multirun.launcher.agent.info': '所有运行使用同一智能体。默认使用你当前配置的智能体。', 'multirun.launcher.setupCommands.label': '设置命令', diff --git a/packages/ui/src/stores/useMultiRunStore.ts b/packages/ui/src/stores/useMultiRunStore.ts index b11ae28b..afdbccbc 100644 --- a/packages/ui/src/stores/useMultiRunStore.ts +++ b/packages/ui/src/stores/useMultiRunStore.ts @@ -118,10 +118,11 @@ export const useMultiRunStore = create()( const directory = project.path; const isGit = await checkIsGitRepository(directory); + const shouldIsolateRuns = isGit && params.isolateRuns !== false; const groupSlug = toGitSafeSlug(groupName); - const rootBranch = isGit ? await getRootBranch(directory) : undefined; - const rootTrackingRemote = isGit ? await resolveRootTrackingRemote(directory) : null; + const rootBranch = shouldIsolateRuns ? await getRootBranch(directory) : undefined; + const rootTrackingRemote = shouldIsolateRuns ? await resolveRootTrackingRemote(directory) : null; const createdRuns: Array<{ sessionId: string; @@ -161,7 +162,7 @@ export const useMultiRunStore = create()( ? `${groupSlug}/${model.providerID}/${model.modelID}/${index}` : `${groupSlug}/${model.providerID}/${model.modelID}`; - if (!isGit) { + if (!shouldIsolateRuns) { const session = await opencodeClient.withDirectory( directory, () => opencodeClient.createSession({ title: sessionTitle }) diff --git a/packages/ui/src/types/multirun.ts b/packages/ui/src/types/multirun.ts index 5ba23c39..8fa2a051 100644 --- a/packages/ui/src/types/multirun.ts +++ b/packages/ui/src/types/multirun.ts @@ -32,6 +32,8 @@ export interface CreateMultiRunParams { agent?: string; /** Base branch for new branches (defaults to `HEAD`). */ worktreeBaseBranch?: string; + /** Whether Git projects should isolate each run in its own worktree. */ + isolateRuns?: boolean; /** Files to attach to all runs */ files?: MultiRunFileAttachment[]; /** Setup commands to run in each new worktree after creation */