diff --git a/web/src/components/session-wizard/SessionWizard.tsx b/web/src/components/session-wizard/SessionWizard.tsx index 199ea7651..9b61ad572 100644 --- a/web/src/components/session-wizard/SessionWizard.tsx +++ b/web/src/components/session-wizard/SessionWizard.tsx @@ -4,6 +4,7 @@ import { fetchAgents, fetchGroups, fetchDockerStatus, fetchProfiles, fetchSettin import { StepIndicator } from "./StepIndicator"; import type { StepDef, StepId } from "./StepIndicator"; import { ProjectStep } from "./steps/ProjectStep"; +import { SessionStep } from "./steps/SessionStep"; import { AgentStep } from "./steps/AgentStep"; import { ReviewStep } from "./steps/ReviewStep"; import { applyBranchOverride, getSubmittedBranch } from "./sessionNames"; @@ -13,6 +14,7 @@ export interface WizardData { title: string; worktreeBranch: string; worktreeBranchDirty: boolean; + useWorktree: boolean; group: string; tool: string; profile: string; @@ -54,6 +56,7 @@ type Action = const initialData: WizardData = { path: "", title: "", worktreeBranch: "", worktreeBranchDirty: false, + useWorktree: true, group: "", tool: "claude", profile: "", yoloMode: false, sandboxEnabled: false, sandboxImage: "", extraEnv: [], advancedEnabled: false, profileDirty: false, @@ -114,11 +117,12 @@ function reducer(state: WizardState, action: Action): WizardState { } } -// Wizard: project path → agent/settings → review +// Wizard: project path → session (title + worktree) → agent → review function computeSteps(_data: WizardData): StepDef[] { return [ { id: "project", label: "Project" }, - { id: "agent", label: "Settings" }, + { id: "session", label: "Session" }, + { id: "agent", label: "Agent" }, { id: "review", label: "Review" }, ]; } @@ -156,7 +160,7 @@ export function SessionWizard({ onClose, onCreated, prefill }: Props) { : initialData; const [state, dispatch] = useReducer(reducer, { - currentStep: prefill?.skipToReview ? 2 : (prefill?.path ? 1 : 0), + currentStep: prefill?.skipToReview ? 3 : (prefill?.path ? 1 : 0), data: prefillData, isSubmitting: false, error: null, agents: [], groups: [], profiles: [], dockerAvailable: false, }); @@ -201,8 +205,8 @@ export function SessionWizard({ onClose, onCreated, prefill }: Props) { path: d.path, tool: d.tool, title: d.title || undefined, group: d.group || undefined, yolo_mode: d.yoloMode, - worktree_branch: getSubmittedBranch(d.title, d.worktreeBranch), - create_new_branch: true, + worktree_branch: d.useWorktree ? getSubmittedBranch(d.title, d.worktreeBranch) : undefined, + create_new_branch: d.useWorktree, sandbox: d.sandboxEnabled, sandbox_image: d.sandboxEnabled ? d.sandboxImage : undefined, extra_env: d.sandboxEnabled && d.extraEnv.length > 0 ? d.extraEnv.filter(Boolean) : undefined, @@ -224,6 +228,8 @@ export function SessionWizard({ onClose, onCreated, prefill }: Props) { switch (currentStepDef?.id) { case "project": return ; + case "session": + return ; case "agent": return ( {isHostOnly && ( -

{selectedAgent?.name} can only run on the host. Container option is disabled.

+

+ {selectedAgent?.name} can only run on the host. Container is disabled + {data.useWorktree ? "; go back and turn off “Create a worktree” too." : "."} +

)} {/* Advanced settings (collapsible) */} @@ -207,31 +210,6 @@ export function AgentStep({ data, onChange, agents, profiles, dockerAvailable, o {showAdvanced && (
- {/* Session title / branch */} -
- - onChange("title", e.target.value)} - placeholder="Shown in Agent of Empires" - className="w-full bg-surface-900 border border-surface-700 rounded-lg px-3 py-2.5 text-base font-mono text-text-primary placeholder:text-text-dim focus:border-brand-600 focus:outline-none" - /> -

Shown in the dashboard and session lists. Renaming it later does not rename the git branch.

-
- -
- - onChange("worktreeBranch", e.target.value)} - placeholder="Uses session title if empty" - className="w-full bg-surface-900 border border-surface-700 rounded-lg px-3 py-2.5 text-base font-mono text-text-primary placeholder:text-text-dim focus:border-brand-600 focus:outline-none" - /> -

Git branch and worktree name. Clearing it switches back to the session title. Leave both title and branch empty to auto-generate.

-
- {/* Container config (if sandbox enabled) */} {data.sandboxEnabled && ( <> @@ -309,18 +287,6 @@ export function AgentStep({ data, onChange, agents, profiles, dockerAvailable, o className="w-full bg-surface-900 border border-surface-700 rounded-lg px-3 py-2.5 text-sm font-mono text-text-primary placeholder:text-text-dim focus:border-brand-600 focus:outline-none" />
- - {/* Group */} -
- - onChange("group", e.target.value)} - placeholder="Optional session group" - className="w-full bg-surface-900 border border-surface-700 rounded-lg px-3 py-2.5 text-sm font-mono text-text-primary placeholder:text-text-dim focus:border-brand-600 focus:outline-none" - /> -
)} diff --git a/web/src/components/session-wizard/steps/ReviewStep.tsx b/web/src/components/session-wizard/steps/ReviewStep.tsx index 729aad5a5..fe4bc5980 100644 --- a/web/src/components/session-wizard/steps/ReviewStep.tsx +++ b/web/src/components/session-wizard/steps/ReviewStep.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react"; import type { StepDef, StepId } from "../StepIndicator"; import { getReviewSummary } from "../sessionNames"; -interface WizardData { path: string; title: string; worktreeBranch: string; group: string; tool: string; profile: string; profileDirty: boolean; yoloMode: boolean; sandboxEnabled: boolean; sandboxImage: string; extraArgs: string; customInstruction: string; commandOverride: string; [key: string]: unknown; } +interface WizardData { path: string; title: string; worktreeBranch: string; useWorktree: boolean; group: string; tool: string; profile: string; profileDirty: boolean; yoloMode: boolean; sandboxEnabled: boolean; sandboxImage: string; extraArgs: string; customInstruction: string; commandOverride: string; [key: string]: unknown; } interface Props { data: WizardData; onChange: (field: string, value: unknown) => void; isSubmitting: boolean; error: string | null; onSubmit: () => void; onJumpTo: (stepId: StepId) => void; steps: StepDef[]; } const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.userAgent); @@ -122,14 +122,18 @@ export function ReviewStep({ data, onChange, isSubmitting, error, onSubmit, onJu placeholder="Auto-generated" onChange={(v) => onChange("title", v)} /> - onChange("worktreeBranch", v)} - accent - /> + {data.useWorktree ? ( + onChange("worktreeBranch", v)} + accent + /> + ) : ( + + )} {data.profile && ( diff --git a/web/src/components/session-wizard/steps/SessionStep.tsx b/web/src/components/session-wizard/steps/SessionStep.tsx new file mode 100644 index 000000000..11dfa7e2a --- /dev/null +++ b/web/src/components/session-wizard/steps/SessionStep.tsx @@ -0,0 +1,96 @@ +interface WizardData { + title: string; + worktreeBranch: string; + useWorktree: boolean; + group: string; + tool: string; + [key: string]: unknown; +} + +interface Props { + data: WizardData; + onChange: (field: string, value: unknown) => void; +} + +function Toggle({ checked, onChange, disabled }: { checked: boolean; onChange: (v: boolean) => void; disabled?: boolean }) { + return ( + + ); +} + +export function SessionStep({ data, onChange }: Props) { + return ( +
+

Name your session

+

Give it a title and decide whether to work in a git worktree.

+ +
+ + onChange("title", e.target.value)} + placeholder="Auto-generated if empty" + className="w-full bg-surface-900 border border-surface-700 rounded-lg px-3 py-2.5 text-base font-mono text-text-primary placeholder:text-text-dim focus:border-brand-600 focus:outline-none" + /> +

Shown in the dashboard. Renaming it later does not rename the git branch.

+
+ + + + {data.useWorktree && ( +
+ + onChange("worktreeBranch", e.target.value)} + placeholder="Uses session title if empty" + className="w-full bg-surface-900 border border-surface-700 rounded-lg px-3 py-2.5 text-base font-mono text-text-primary placeholder:text-text-dim focus:border-brand-600 focus:outline-none" + /> +

The branch name is also the worktree directory name. Leave blank to use the session title.

+
+ )} + +
+ + onChange("group", e.target.value)} + placeholder="Optional, for organizing related sessions" + className="w-full bg-surface-900 border border-surface-700 rounded-lg px-3 py-2.5 text-sm font-mono text-text-primary placeholder:text-text-dim focus:border-brand-600 focus:outline-none" + /> +
+
+ ); +}